Git Product home page Git Product logo

wire's Introduction

Wire: Automated Initialization in Go

Build Status godoc Coverage

Wire is a code generation tool that automates connecting components using dependency injection. Dependencies between components are represented in Wire as function parameters, encouraging explicit initialization instead of global variables. Because Wire operates without runtime state or reflection, code written to be used with Wire is useful even for hand-written initialization.

For an overview, see the introductory blog post.

Installing

Install Wire by running:

go install github.com/google/wire/cmd/wire@latest

and ensuring that $GOPATH/bin is added to your $PATH.

Documentation

Project status

As of version v0.3.0, Wire is beta and is considered feature complete. It works well for the tasks it was designed to perform, and we prefer to keep it as simple as possible.

We'll not be accepting new features at this time, but will gladly accept bug reports and fixes.

Community

For questions, please use GitHub Discussions.

This project is covered by the Go Code of Conduct.

wire's People

Contributors

a8m avatar benhoyt avatar budougumi0617 avatar cflewis avatar clausti avatar clivern avatar cristaloleg avatar dmitris avatar doodlesbykumbi avatar efueyo avatar eliben avatar ernado avatar giautm avatar ijt avatar jan-hajek avatar josgilmo avatar ktr0731 avatar kzh125 avatar mwlazlo avatar rsc avatar shantuo avatar steebchen avatar stytchiz avatar toddtreece avatar u5surf avatar vangent avatar wolfogre avatar yashigani avatar zkry avatar zombiezen 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wire's Issues

wire: `go get`-ing the wire cmd fails inside of a go module

$ go version
go version go1.11beta2 linux/amd64
~$ mkdir example
~$ cd example 
~/example$ echo "module example" > go.mod
~/example$ go get github.com/google/go-cloud/wire/cmd/wire
go: finding github.com/google/go-cloud/wire/cmd/wire latest
go: finding github.com/google/go-cloud/wire/cmd latest
go: finding github.com/google/go-cloud/wire latest
go get github.com/google/go-cloud/wire/cmd/wire: cannot find module providing package github.com/google/go-cloud/wire/cmd/wire
~/example$ which wire
wire not found

wire: interface type in Value expression not available at runtime

If a user writes something like:

var MySet = wire.NewSet(wire.Value(http.Handler(nil)))

or even:

var MySet = wire.NewSet(wire.Value(io.Reader(os.Stdin)))

Then the interface type information isn't available at runtime. This was the other problem I identified in #98, but is not being addressed by #104. I don't have a good answer for how to fix this one, though.

wire: add "prune" command

Since Wire knows the minimal set of provider functions and wire.Value expressions it needs to generate the injector, we could introduce a command that trims out any provider sets from a wire.Use statement that aren't fully used and replaces them with exactly the elements that do get fully used. There could also be a more aggressive option that inlines all the provider functions and wire.Value expressions that are needed, to make it so that the injector template's imports map 1:1 with the generated code.

This would require similar logic to #51, but the usage is different. A subtract command is used for overriding providers, but a prune command is used for tightening dependencies.

h/t @neild for the idea.

wire: add generated line numbers

wire_gen.go copies over declarations from the injector template. However, if there is a problem, the developer should modify the injector template source, not the generated code. To ensure stack traces and compiler errors show up properly, gowire should emit //line directives.

wire: unused provider error when only pointer to struct is used

Repro case:

package main

import "github.com/google/go-cloud/wire"

type Foo struct{}

func inject() *Foo {
  wire.Build(Foo{})
  return nil
}

This produces the error:

foo.go:7:1: inject inject: unused provider "Foo"
wire: generate failed

I expect this to succeed.

wire: incrementally verify provider sets

From a TODO I added:

https://github.com/google/go-cloud/blob/bf596b4c8d80a30a6087a75426f84842d6d14c6d/goose/internal/goose/analyze.go#L236

Instead of doing analysis/validation at the very end of generation, it would be better to do in a more modular way, such that each call to *objectCache.processNewSet is guaranteed to return a provider set that...

  • ...does not include cycles.
  • ...does not include multiple bindings for one output type.
  • ...does not have bindings to concrete types not in the provider set. (Technically a new restriction, but one that does not break existing code in this repository.)

End-user benefit is that errors are reported much closer to when they occur and provider sets returned through the package API can be guaranteed to have valid, query-able graphs. This should hopefully remove a bunch of semi-duplicated code from wire show.

Present wire as a go generate tool, not a DI framework

In the Go community, "framework" is cause for tremendous skepticism. And with "Dependency Injection" as a modifier, wire is positioning itself to be dismissed outright by many, when in fact wire is more like a go generate tool.

One other change would be to swap the use of "providers" with "initializers."

wire: types get lowercased to generate var names, which can conflict with keywords

What did you do?

Attempted to use wire to inject an object of type rest.Interface (from k8s client-go).
Repro available here https://github.com/landism/go-cloud/tree/landism/keyword_bug/wire/bug
(cd wire and run wire ./bug)

What did you expect to see?

wire successfully create a file named wire_gen.go that constructs a Foo.

What did you see instead?

$ wire ./bug
11:2: expected expression (and 5 more errors)
wire: generate failed
  1. wire generates code containing interface := ProvideInterface(). Since interface is a go keyword, go fails to parse this when wire tries to fmt its output.
  2. aside from the problems with selecting the name interface, it might also be worth fixing the error output (not sure if this is worth a separate issue). To figure out why wire wasn't working for me, I modified wire to output the pre-formatted source, which I then fed directly into gofmt which gave me all five errors instead of just the first, though they still wouldn't have been actionable if I hadn't modified wire to get the unformatted source.

Does this issue reproduce with the latest release (go1.11)?

I repro'd when building wire from HEAD of master, but I haven't tried with go1.11.

System details

go version go1.10.3 darwin/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/matt/Library/Caches/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/matt/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.10.3/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.10.3/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
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/0t/c8t9m67j7qs4y021nl5_3_yr0000gp/T/go-build546913191=/tmp/go-build -gno-record-gcc-switches -fno-common"
GOROOT/bin/go version: go version go1.10.3 darwin/amd64
GOROOT/bin/go tool compile -V: compile version go1.10.3
uname -v: Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64
ProductName:	Mac OS X
ProductVersion:	10.13.6
BuildVersion:	17G65
lldb --version: lldb-902.0.79.7
  Swift-4.1

wire: caching fails to make tests run instantaneously

issactrotts-macbookpro:go-cloud issactrotts$ time vgo test ./wire/internal/wire/...

ok      github.com/google/go-cloud/wire/internal/wire   35.005s

real    2m29.450s
user    1m45.080s
sys     1m41.907s
issactrotts-macbookpro:go-cloud issactrotts$ time vgo test ./wire/internal/wire/...
ok      github.com/google/go-cloud/wire/internal/wire   (cached)

real    0m37.605s
user    0m13.383s
sys     0m24.832s

It should take no more than a second to run the tests in the cached case, but really it should be on the order of 100 milliseconds or less.

The problem appears to be that there are many subtests that are dynamically generated. The dynamic generation takes a while, and apparently has to happen regardless of whether there are cache hits for all the tests.

An alternative that would probably allow caching to operate normally would be to generate the test code.

wire: proposal to separate wire into its own module and own repository

Go version

go version go1.11 linux/amd64

What did you do?

Add wire as a dependency to a Go project that is using Go modules.

$ go get github.com/google/go-cloud/wire/cmd/wire

What did you expect to see?

Download only wire and it's dependencies, which according to the following command, is only two packages (ignoring dependencies within the Go stdlib):

  1. github.com/google/go-cloud/wire/internal/wire
  2. golang.org/x/tools/go/types/typeutil

The list of dependencies were determined by running the following command:

$ cd $GOPATH/src/github.com/google/go-cloud/wire/cmd/wire
$ go list -f '{{ join .Deps "\n" }}' . | grep '\.'

What did you see instead?

Go inspecting many packages not used by wire.

go: finding github.com/google/go-cloud/wire/cmd/wire latest
go: finding github.com/google/go-cloud/wire/cmd latest
go: finding github.com/google/go-cloud/wire latest
go: finding github.com/google/go-cloud v0.2.0
go: downloading github.com/google/go-cloud v0.2.0
go: finding github.com/census-ecosystem/opencensus-go-exporter-aws v0.0.0-20180411051634-41633bc1ff6b
go: finding github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2
go: finding github.com/davecgh/go-spew v1.1.0
go: finding github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20180321230639-1e456b1c68cb
go: finding github.com/google/martian v2.0.0-beta.2+incompatible
go: finding github.com/gorilla/mux v1.6.1
go: finding github.com/jtolds/gls v0.0.0-20170503224851-77f18212c9c7
go: finding contrib.go.opencensus.io/exporter/stackdriver v0.0.0-20180421005815-665cf5131b71
go: finding gopkg.in/ini.v1 v1.37.0
go: finding cloud.google.com/go v0.0.0-20180816152847-43dc61c3e9d0
go: finding github.com/google/go-cmp v0.2.0
go: finding github.com/smartystreets/assertions v0.0.0-20180301161246-7678a5452ebe
go: finding github.com/aws/aws-xray-sdk-go v1.0.0-rc.5
go: finding golang.org/x/tools v0.0.0-20180314180217-d853e8088c62
go: finding golang.org/x/net v0.0.0-20180702212446-ed29d75add3d
go: [email protected]: unrecognized import path "go.opencensus.io" (https fetch: Get https://go.opencensus.io?go-get=1: unexpected EOF)
go: finding golang.org/x/crypto v0.0.0-20180718160520-a2144134853f
go: finding google.golang.org/grpc v1.13.0
go: finding github.com/gorilla/context v1.1.1
go: finding google.golang.org/api v0.0.0-20180606215403-8e9de5a6de6d
go: finding github.com/golang/protobuf v1.1.0
go: finding golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
go: finding github.com/fsnotify/fsnotify v1.4.7
go: finding github.com/smartystreets/gunit v0.0.0-20180314194857-6f0d6275bdcd
go: finding gopkg.in/pipe.v2 v2.0.0-20140414041502-3c2ca4d52544
go: finding github.com/aws/aws-sdk-go v1.13.20
go: finding github.com/gopherjs/gopherjs v0.0.0-20180424202546-8dffc02ea1cb
go: finding golang.org/x/oauth2 v0.0.0-20180603041954-1e0a3fa8ba9a
...

Proposal

It seems that depending on wire depends on many other packages because it is contained as part of the large go-cloud module, even though wire has only two dependencies outside the stdlib.

Even if dependency managers (e.g. go mod, go get, dep, etc) are improved to deal with this situation better and only download the dependencies of the specific package being used, it's overwhelming from a conceptual, external and non-Googler point-of-view to look at this repository for wire when this repository is largely not about wire.

wire appears to be a lightweight, standalone and isolated piece of technology that would be useful to many Go projects that have nothing to do with the cloud, e.g. CLIs. Can it be broken out as a separate module/package?

(If this is not something that is desirable, what are the long term plans for wire? Is it intended to become more cloud focused? Should the Go community avoid using it for non-cloud projects?)

wire: fails with wasm errors with go 1.11

After upgrading to go 1.11, when I run wire, I get errors like below. Updating wire doesn't seem to help

$ wire .
/usr/local/go/src/internal/cpu/cpu_x86.go:9:7: CacheLineSize redeclared in this block
/usr/local/go/src/internal/cpu/cpu_wasm.go:7:7: 	other declaration of CacheLineSize
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:8:2: ArchFamily redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:8:2: 	other declaration of ArchFamily
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:9:2: BigEndian redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:9:2: 	other declaration of BigEndian
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:10:2: CacheLineSize redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:10:2: 	other declaration of CacheLineSize
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:11:2: DefaultPhysPageSize redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:11:2: 	other declaration of DefaultPhysPageSize
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:12:2: PCQuantum redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:12:2: 	other declaration of PCQuantum
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:13:2: Int64Align redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:13:2: 	other declaration of Int64Align
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:14:2: HugePageSize redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:14:2: 	other declaration of HugePageSize
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:15:2: MinFrameSize redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:15:2: 	other declaration of MinFrameSize
/usr/local/go/src/runtime/internal/sys/arch_wasm.go:18:6: Uintreg redeclared in this block
/usr/local/go/src/runtime/internal/sys/arch_amd64.go:18:6: 	other declaration of Uintreg
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:14:6: Load redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:13:6: 	other declaration of Load
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:20:6: Loadp redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:19:6: 	other declaration of Loadp
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:26:6: Load64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:25:6: 	other declaration of Load64
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:32:6: Xadd redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:30:6: 	other declaration of Xadd
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:40:6: Xadd64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:33:6: 	other declaration of Xadd64
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:48:6: Xadduintptr redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:36:6: 	other declaration of Xadduintptr
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:56:6: Xchg redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:39:6: 	other declaration of Xchg
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:64:6: Xchg64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:42:6: 	other declaration of Xchg64
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:72:6: Xchguintptr redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:45:6: 	other declaration of Xchguintptr
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:80:6: And8 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:48:6: 	other declaration of And8
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:86:6: Or8 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:51:6: 	other declaration of Or8
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:94:6: Cas64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:56:6: 	other declaration of Cas64
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:104:6: Store redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:59:6: 	other declaration of Store
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:110:6: Store64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:62:6: 	other declaration of Store64
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:116:6: StorepNoWB redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_amd64x.go:68:6: 	other declaration of StorepNoWB
/usr/local/go/src/runtime/internal/atomic/stubs.go:12:6: Cas redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:122:6: 	other declaration of Cas
/usr/local/go/src/runtime/internal/atomic/stubs.go:15:6: Casp1 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:132:6: 	other declaration of Casp1
/usr/local/go/src/runtime/internal/atomic/stubs.go:18:6: Casuintptr redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:142:6: 	other declaration of Casuintptr
/usr/local/go/src/runtime/internal/atomic/stubs.go:21:6: Storeuintptr redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:152:6: 	other declaration of Storeuintptr
/usr/local/go/src/runtime/internal/atomic/stubs.go:24:6: Loaduintptr redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:158:6: 	other declaration of Loaduintptr
/usr/local/go/src/runtime/internal/atomic/stubs.go:27:6: Loaduint redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:164:6: 	other declaration of Loaduint
/usr/local/go/src/runtime/internal/atomic/stubs.go:32:6: Loadint64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:170:6: 	other declaration of Loadint64
/usr/local/go/src/runtime/internal/atomic/stubs.go:35:6: Xaddint64 redeclared in this block
/usr/local/go/src/runtime/internal/atomic/atomic_wasm.go:176:6: 	other declaration of Xaddint64
/usr/local/go/src/runtime/sys_x86.go:16:6: gostartcall redeclared in this block
/usr/local/go/src/runtime/sys_wasm.go:31:6: 	other declaration of gostartcall
wire: generate failed

wire: document how to bind interfaces to interfaces

Although returning interfaces from functions is not idiomatic in Go, it would be nice to support it in Wire.

Example use case:

// Consumer
package foo

type Foo interface {
  ...
}

// Producer 1
package one

type FooOne foo.Foo

func New() FooOne {
  ...
}

var Set = wire.NewSet(New)

// Producer 2
package two

type FooTwo foo.Foo

func New() FooTwo {
  ...
}

var Set = wire.NewSet(New)

// Somewhere else
package composite

type FooComposite foo.Foo

func New(fooOne one.FooOne, fooTwo two.FooTwo) FooComposite {
  ...
}

var SuperSet = wire.NewSet(
    one.Set,
    two.Set,
    New,
    // This currently doesn't work; there is no way to bind an interface to another interface.
    wire.Bind(new(foo.Foo), new(FooComposite)))

Perhaps this is what wire.InterfaceValue() is intended for? (I wasn't able to get it to work.)

wire/tests: Allow wire_errs.txt to match multi-line errors, and to use regexes

Current the wire tests verify that the whole line from wire_errs.txt is found in one of the actual errors. It doesn't work well for errors that span multiple lines (e.g., the err.String() has \n's in it). In addition, some errors have things like line numbers that are fragile to include, maybe a regex match would be more flexible.

wire: replace wire.Value with wire.Zero

In reviewing the Wire documentation, I think that the variable reference semantics in wire.Value expressions are too subtle, and are impossible to precisely reproduce if a runtime-based approach is ever used. Out of the 9 current non-test usages of the wire.Value, only 2 use a non-zero value. The first is for http.DefaultClient and the second is for a statically known list of health checks. It's simpler to understand both of these as providers at the expense of some typing.

For the common (and originally intended) use-case, I propose replacing wire.Value with wire.Zero, defined as:

// Zero binds a type to its zero value. ptr must be a pointer to the desired type.
//
// Example:
//
//     var MySet = wire.NewSet(wire.Zero(new(*MyStruct)))
func Zero(ptr interface{}) ZeroValue {
  // ...
}

// A ZeroValue is a binding to a zero value.
type ZeroValue struct{}

wire: turn tests into replay tests

@vangent had an excellent suggestion about how to speed up Wire tests: save a copy of the "golden" code generation and add a record mode.

Currently, the Wire tests are split into Generate (runs go build) and Determinism (runs Wire twice and compares for equality). @vangent observed the similarity between this and our other replay tests, and suggested to merge this into one test: Generate, which by default would read from the golden file. It can be passed a -record flag to create the golden file, which will be run against go build to ensure that the output matches the expected output.

wire: only evaluate Value expression at injector package initialization time

In #98, I proposed eliminating the wire.Value directive because of its somewhat confusing (and non-runtime-replicatable) behavior of re-evaluating the expression on every call to the injector. @rsc suggested a better compromise: the used wire.Value expressions should be copied as unexported package variables in the generated file, then read by the injector function.

While it is possible that someone could write to a variable between the time the wire.Value's argument is evaluated and the time that generated package variable initializer is evaluated, it is likely bad practice. Further, this new behavior is much more predictable: the expression will be evaluated at most twice and always before main() starts, instead of N+1 where N is the number of calls to the injector function.

wire: functions calling wire.Build after assigning a variable are silently erased in output

This came up in the following situation:

// setupGCP is a Wire injector function that sets up the application using GCP.
func setupGCP(ctx context.Context, flags *cliFlags) (*application, func(), error) {
        traceOpt := withNoTraceOptions()
        // This will be filled in by Wire with providers from the provider sets in
        // wire.Build.
        wire.Build(
                gcpcloud.GCP,
                applicationSet,
                gcpBucket,
                gcpMOTDVar,
                gcpSQLParams,
                wire.Value(traceOpt),
        )
        return nil, nil, nil
}

func withNoTraceOptions() ocsql.TraceOption {
        return func(o *ocsql.TraceOptions) {}
}

Running go generate here resulted in wire_gen.go not containing a setupGCP function anymore, because of the assignment to traceOpt.

This assignment was made necessary by Wire's rejection of wire.Value(withNoTraceOptions()) since it contains a function call and is therefore too complex by Wire's standards. Maybe the error message should suggest that such assignments be moved out of the injector function and placed in package scope.

wire: accept alternate forms of injector template

To avoid bikeshedding and new user confusion, Wire should allow for forms of injector function templates other than the panic(wire.Use(...)) syntax.

The concrete suggestions were:

func injectorTemplate() *app {
  wire.Use(...)
}

The above does not type-check, which may confuse IDEs and tooling. Wire would need to special-case ignoring the type-check errors inside the template body. I'd also like to permit:

func injectorTemplate() *app {
  wire.Use(...)
  return nil
}

/cc @cflewis @rsc

wire: wire.FieldsOf() to inject the values from fields of a struct

Pretty new to wire, so forgive me if I am asking the wrong questions.

As far as I understand, wire.Value() allows to specify a value to be injected. If I have a struct, the fields of which must be injected rather than the value of the struct itself, would I have to use wire.Value() for each field of the struct?

Would it make sense to add something like wire.FieldValues(), which injects the fields of the struct that is specified rather than the struct itself?

wire: improve package documentation

The package documentation should definitiively document the semantics of the wire functions. Currently the documentation is pretty thin and relies a lot on the introduction document. For example, the doc comment for Build says "its arguments are the same as NewSet", and although the doc comment for NewSet describes the possible kinds of parameters, it doesn't say what they mean. I believe that it should be possible to read the godoc and understand how to use wire and read code that uses it.

wire: make wire package publicly import-able

Other static analysis tools might want to get access to the Wire provider graph for visualization, navigation, etc. The package is currently internal so that the API can be iterated on quickly, but once it's baked, it would be good to allow others to use it.

wire: Issues with struct providers

I created a simple example project with go 1.11 here. Here's what I'm seeing currently:

~/wiretest $ cd container 
~/wiretest/container $ wire
/Users/me/wiretest/container/container.go:21:1: inject New: unused provider "Container"
wire: generate failed

If, however, I replace the struct provider with an explicit provider, it works.

~/wiretest $ git diff
diff --git a/container/container.go b/container/container.go
index c813de4..32eacf6 100644
--- a/container/container.go
+++ b/container/container.go
@@ -14,15 +14,14 @@ type Container struct {
        Bar *bar.Bar
 }
 
-// func newContainer(foo *foo.Foo, bar *bar.Bar) *Container {
-//     return &Container{foo, bar}
-// }
+func newContainer(foo *foo.Foo, bar *bar.Bar) *Container {
+       return &Container{foo, bar}
+}
 
 func New() *Container {
        panic(wire.Build(
                foo.New,
                bar.New,
-               Container{},
-               // newContainer,
+               newContainer,
        ))
 }
~/wiretest $ cd container 
~/wiretest/container $ wire
~/wiretest/container $ cd ..
~/wiretest $ go run main.go 
&{0x117ff88 0xc00000c028}

This seems to be an issue with struct providers, unless I'm doing something wrong.

wire: report all dependency graph errors at once

Right now, Wire bails on the first missing dependency or other such graph error. It should try to collect as many errors as it can then report them in one go. This would reduce the number of times that a user needs to re-run gowire during initial setup.

wire: rewrite test cases to use new return-based form

As of 0142b8eb69e25e8ecf0e4717b96e2b21585d8b19, the Wire documentation recommends return-based injector templates. The test cases have not been updated to the new norm, so should be updated to reflect idiomatic usage.

wire: support loading type-checking with Go modules

Wire uses golang.org/x/tools/go/loader to get type information, but this does not use Go modules. The current workaround is to run vgo mod -vendor, but this occasionally requires adding more imports temporarily to pin all the dependencies (especially when using awscloud.AWS or gcpcloud.GCP). There isn't a good way to solve this until Go module tooling hits mainstream, but I'm keeping this issue here to track.

wire: consider splitting "wire" into a separate repository

"wire" is relatively complex, and only tangentially related to Go Cloud. It may also be useful for dependency injection use cases that are not using Go Cloud. We should consider splitting it into a separate repository to make that clear.

Related: the documentation in Go Cloud should make it more clear what wire's role is. E.g., I'd suggest that we have a simple example app that doesn't use wire, and a more complex one with multiple dependencies that does wire, and shows in comments what the code would have looked like without it.

wire: improve the names of the generated variables.

Right now wire names the variables holding provided values based on their types and if there are collisions in those names, it adds a counter to the name. So if there are Clients coming from 3 different packages, we'd have client, client2, client3.

I think the generated code could be better if the package is also taken into consideration when naming a variable. For instance if we have redis.Client and mongo.Client, the variables should be redisClient and mongoClient instead of client and client2.

Edit.: Looking at the code, there seems to already be a todo for this:
https://github.com/google/go-cloud/blob/master/wire/internal/wire/wire.go#L676

wire: document why wire doesn't allow duplicate identical providers in a provider set

wire.NewSet() is the perfect terminology. By that definition, this (albeit contrived example) should ideally work:

var Set = wire.NewSet(ProvideFoo, ProvideFoo)

Currently, this fails with a multiple bindings error. While you certainly want to prohibit the application from defining bindings in conflicting ways, automatically deduplicating identical bindings would simplify application development with Wire.

A more interesting example is when you have provider sets at multiple levels of the stack with inter-dependencies:

                 ----------------
                /| feature1.Set |\
               / ---------------- \   ----------------
              /                    ---| library1.Set |
------------ /   ---------------- /   ----------------
| main.Set | ----| feature2.Set |/
------------ \   ----------------
              \
               \ ----------------     ----------------
                \| feature3.Set |-----| library2.Set |
                 ----------------     ----------------

Without deduplication, main.Set has to list all of the Sets in the entire application:

package main
var Set = wire.Set(
    feature1.Set,
    feature2.Set,
    feature3.Set,
    library1.Set,
    library2.Set)

This "top level aggregation" pattern requires knowledge of the entire tree of Sets at the top level, and can be a maintenance hazard as Sets are removed / reconfigured (i.e. dependency rot). This is a common problem with other DI frameworks like Guice.

With deduplication, each Set can be self-contained by including the Sets of their dependencies, even shared ones. Then the top level need only include Sets for its high level features:

package feature1
var Set = wire.Set(..., library1.Set)

package feature2
var Set = wire.Set(..., library1.Set)

package feature3
var Set = wire.Set(..., library2.Set)

package main
var MainSet = wire.Set(
    feature1.Set,
    feature2.Set,
    feature3.Set)

This provides better encapsulation, giving each package the sovereignty to add and remove downstream dependencies without requiring modifications to their upstream dependents.

Long story short: deduplication of identical bindings can allow applications to declare (possibly shared) provider set dependencies, improving encapsulation and making application "wiring" closer to dependency-aware build systems like Bazel.

I hope this provides some good context for this feature request. Thanks!

wire: add "subtract" command

If someone wants to "override" a set of providers from a larger set, it would be helpful to have the gowire tool output the minimal code to create a new provider set that "carves out" the given provider from the set. Ideal workflow:

$ gowire subtract github.com/google/go-cloud/gcp/gcpcloud.GCP github.com/google/go-cloud/gcp.ProjectID
import (
  "github.com/google/go-cloud/gcp"
  "github.com/google/go-cloud/gcp/gcpcloud"
  "github.com/google/go-cloud/wire"
)

var Set = wire.NewSet(
  gcpcloud.Services,
  gcp.CredentialsTokenSource,
  gcp.DefaultCredentials,
)

Perhaps this would include an interactive mode that shows you everything in the provider set and lets you pick which types to remove by menu, since typing type names is arduous.

goose: rename Goose

Before launching, Goose might need a different name. I don't know what this name should be, but I'm keeping the issue open so we don't forget about it.

gowire reports "cannot find package" for various pkgs after running vgo mod -vendor

Here's my session. vgo installs for example fsnotify, but then gowire doesn't find it.

issactrotts-macbookpro:~ issactrotts$ git clone [email protected]:google/go-cloud.git
issactrotts-macbookpro:~ issactrotts$ cd go-cloud
issactrotts-macbookpro:go-cloud issactrotts$ vgo install ./wire/cmd/gowire
issactrotts-macbookpro:go-cloud issactrotts$ cd samples/guestbook/
issactrotts-macbookpro:guestbook issactrotts$ vgo mod -vendor
go: downloading github.com/dnaeon/go-vcr v0.0.0-20180504081357-f8a7e8b9c630
go: downloading github.com/stretchr/testify v1.2.1
go: downloading github.com/go-sql-driver/mysql v0.0.0-20180308100310-1a676ac6e4dc
go: downloading github.com/census-ecosystem/opencensus-go-exporter-aws v0.0.0-20180411051634-41633bc1ff6b
go: downloading github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20180321230639-1e456b1c68cb
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/fsnotify/fsnotify v1.4.7
go: downloading github.com/gorilla/mux v1.6.1
go: downloading github.com/davecgh/go-spew v1.1.0
go: downloading github.com/gorilla/context v1.1.1
go: downloading google.golang.org/appengine v1.1.0
go: downloading gopkg.in/ini.v1 v1.37.0
go: downloading github.com/aws/aws-xray-sdk-go v1.0.0-rc.5
go: downloading golang.org/x/sys v0.0.0-20180329131831-378d26f46672
go: downloading gopkg.in/yaml.v2 v2.2.1
go: downloading github.com/google/go-cmp v0.2.0
go: downloading github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a
go: downloading contrib.go.opencensus.io/exporter/stackdriver v0.0.0-20180421005815-665cf5131b71
go: downloading gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
go: downloading github.com/jtolds/gls v0.0.0-20170503224851-77f18212c9c7
go: downloading github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
go: downloading golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
go: downloading github.com/gopherjs/gopherjs v0.0.0-20180424202546-8dffc02ea1cb
go: downloading github.com/smartystreets/assertions v0.0.0-20180301161246-7678a5452ebe
go: downloading github.com/smartystreets/gunit v0.0.0-20180314194857-6f0d6275bdcd

issactrotts-macbookpro:guestbook issactrotts$ gowire
/Users/issactrotts/gopath/src/github.com/google/go-cloud/runtimevar/filevar/filevar.go:46:2: could not import github.com/fsnotify/fsnotify (cannot find package "github.com/fsnotify/fsnotify" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/github.com/fsnotify/fsnotify (from $GOROOT)
	/Users/issactrotts/gopath/src/github.com/fsnotify/fsnotify (from $GOPATH))
/Users/issactrotts/gopath/src/github.com/google/go-cloud/mysql/cloudmysql/cloudmysql.go:25:2: could not import github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs (cannot find package "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs (from $GOROOT)
	/Users/issactrotts/gopath/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs (from $GOPATH))
/Users/issactrotts/gopath/src/github.com/google/go-cloud/mysql/cloudmysql/cloudmysql.go:26:2: could not import github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy (cannot find package "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy (from $GOROOT)
	/Users/issactrotts/gopath/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/proxy (from $GOPATH))
/Users/issactrotts/gopath/src/github.com/google/go-cloud/mysql/cloudmysql/cloudmysql.go:32:4: could not import github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql (cannot find package "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql (from $GOROOT)
	/Users/issactrotts/gopath/src/github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/mysql (from $GOPATH))
/Users/issactrotts/gopath/src/github.com/google/go-cloud/server/xrayserver/server.go:30:11: could not import github.com/census-ecosystem/opencensus-go-exporter-aws (cannot find package "github.com/census-ecosystem/opencensus-go-exporter-aws" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/github.com/census-ecosystem/opencensus-go-exporter-aws (from $GOROOT)
	/Users/issactrotts/gopath/src/github.com/census-ecosystem/opencensus-go-exporter-aws (from $GOPATH))
/Users/issactrotts/gopath/src/github.com/google/go-cloud/server/sdserver/server.go:28:2: could not import contrib.go.opencensus.io/exporter/stackdriver (cannot find package "contrib.go.opencensus.io/exporter/stackdriver" in any of:
	/Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/contrib.go.opencensus.io/exporter/stackdriver (from $GOROOT)
	/Users/issactrotts/gopath/src/contrib.go.opencensus.io/exporter/stackdriver (from $GOPATH))
load: couldn't load packages due to errors: github.com/google/go-cloud/mysql/cloudmysql, github.com/google/go-cloud/server/sdserver, github.com/google/go-cloud/server/xrayserver and 1 more
gowire: generate failed

wire: error messages not showing enough context in cases of conflict

While implementing a new server type, I added a set to an existing wire.Set. The new set had a new provider for server.Server, redundant with one already provided. When I ran go generate, wire issued the following error message:

/home/issactrotts/src/github.com/google/go-cloud/server/jaegerserver/server.go:33:11: multiple bindings for *github.com/google/go-cloud/server.Server (previous binding in "github.com/google/go-cloud/server".Set)

This error message doesn't pinpoint the cause of the problem, i.e., the location where the first server.Set was transitively included. I think it would help to have something akin to a stack trace for this kind of problem.

wire: add check command

gowire should have a check command that verifies that all of the provider sets declared in a package are valid (e.g. NewSet arguments are well-formed, no cycles). This wouldn't entail new logic, but is blocked by #29.

When this is added, we should add a step to our Travis builds that runs gowire check across all packages.

wire: wire.Value is not forbidding references to names introduced in function scope

See
https://github.com/vangent/go-cloud/tree/wirebug/wire/internal/wire/testdata/ValueFromStructField
for an example.

func newMainService(cfg *MainConfig) *MainService {
      wire.Build(
		MainService{},
		foo.New,
		wire.Value(cfg.Foo),
		bar.New,
		wire.Value(cfg.Bar),
		baz.New,
		wire.Value(cfg.Baz),
	)
        return nil
}

results in generated code

            // Injectors from wire.go:                                                                                                                
                                                                                                                                                      
            func newMainService(cfg *MainConfig) *MainService {                                                                                       
                config := _wireConfigValue                                                                                                            
                service := foo.New(config)                                                                                                            
                config2 := _wireConfigValue2                                                                                                          
                service2 := bar.New(config2, service)                                                                                                 
                config3 := _wireConfigValue3                                                                                                          
                service3 := baz.New(config3, service2)                                                                                                
                mainService := &MainService{                                                                                                          
                        Foo: service,                                                                                                                 
                        Bar: service2,                                                                                                                
                        Baz: service3,                                                                                                                
                }
                return mainService
            }

            var (
                _wireConfigValue  = cfg.Foo
                _wireConfigValue2 = cfg.Bar
                _wireConfigValue3 = cfg.Baz
            )

            // wire.go:

which fails with

    wire_test.go:110: go build check failed: build: exit status 2; output:                                                                        
        # example.com/main                                                                                                                        
        ./wire_gen.go:33:22: undefined: cfg                                                                                                       
        ./wire_gen.go:34:22: undefined: cfg                                                                                                       
        ./wire_gen.go:35:22: undefined: cfg 

cfg is out of scope in the generated code.

wire: command should show help when requested

I tried a few things and found no way to get the wire command to show help.

issactrotts-macbookpro:wire issactrotts$ wire
wire: no injector found for .
issactrotts-macbookpro:wire issactrotts$ wire -h
wire: cannot find package "-h" in any of:
        /Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/-h (from $GOROOT)
        /Users/issactrotts/gopath/src/-h (from $GOPATH)
issactrotts-macbookpro:wire issactrotts$ wire help
wire: cannot find package "help" in any of:
        /Users/issactrotts/homebrew/Cellar/go/1.10.2/libexec/src/help (from $GOROOT)
        /Users/issactrotts/gopath/src/help (from $GOPATH)

wire: share dependency graph across injectors in the same package?

I was testing out wire and created a package with two injectors[1]. I noticed that each injector individually recreates the dependency graph. Other DI containers I've used (dagger, for example) allow sharing of singleton objects between different "entry points" to the graph.

Is this behavior intentional or is the goal to eventually allow "reusing" a graph across many injectors within a package?

[1] - https://github.com/drewolson/wiretest/blob/f34107974e871e45ac0f073c7878ab2d6e93b7c4/container/wire_gen.go

wire, server: revisit use of wire to fill in all fields of an Options struct

server.go has an Options struct with a bunch of exported fields in it.

Today, our examples use wire to fill it in, e.g.:
https://github.com/google/go-cloud/blob/master/samples/guestbook/inject_aws.go#L38

That function returns a *application, which needs a *server.Server, which needs a *server.Options; *server.Options is initialized using the Options{} provider. The generated code is here.

In order to build the struct, wire needs to provide all of the exported fields of the struct.

  • A down side of this is that it's no longer possible to add a new field to the Options struct without breaking everyone's "go generate". Note that the previously-generated code will work fine, and use the zero value for the new field, but "go generate" will fail because wire will complain it can't find a provider for the new field.
  • This pattern also only works as long as all of the struct fields have unique types.
  • It also forces the caller to do a somewhat weird incantation using wire.Bind if they just want to leave a field nil (example).
  1. Could wire allow struct providers to leave non-provided fields unset?
  2. We could leave wire alone, but change the examples to have a newOptions() function that is injected more explicitly.
  3. ???

wire: Generated file fails goimports linter

Wire sorts imports alphabetically here, however this does not always translate into lint-valid imports.

E.g.

// +build wireinject

package main

import (
	"context"
	"fmt"
	"time"

	"cloud.google.com/go/civil"
	"github.com/google/go-cloud/wire"
)

func main() {
	fmt.Printf("Running since %s\n", InitializeNow())
}

func ProvideNow(ctx context.Context) civil.Date {
	return civil.DateOf(time.Now())
}

func InitializeNow() civil.Date {
	panic(wire.Build(context.Background, ProvideNow))
}

The generated file has the following

// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

import (
        "cloud.google.com/go/civil"
        "context"
        "fmt"
        "time"
)

// Injectors from main.go:
// ...

Using gometalinter with {"Enable": ["goimports"]} you'll see

$ gometalinter 
wire_gen.go:1::warning: file is not goimported (goimports)

The diff after goimports -w wire_gen.go

import (
-       "cloud.google.com/go/civil"
        "context"
        "fmt"
        "time"
+
+       "cloud.google.com/go/civil"
)

Suggested solution is to update wire.go here

Original

p := filepath.Join(pkgInfo.Dir, "wire_gen.go")
// write

Updated

p := filepath.Join(pkgInfo.Dir, "wire_gen.go")
options := imports.Options{
	Comments:   true, // Default
	TabIndent:  true, // Default
	TabWidth:   8,    // Default
	FormatOnly: true,
}

out, err = imports.Process(p, out, &options) // Uses golang.org/x/tools/imports
// check error then write

If people agree with this approach I can create a PR for it.

wire: provide an example of mocking

It's easy to see how one would use wire in the main.go of an executable to wire up everything it needs, but it's not clear to me how one would go about writing tests with wire. This is simlar to https://github.com/google/go-cloud/issues/223, but in the case of tests, it might be necessary to dynamically construct an object with a mock inserted in an arbitrary point in the graph, potentially dynamically. What's a the recommended way of doing this? Would you just expose the entire graph to the test after it's built so it can take what it needs and manually reassemble new objects from it?

Let me give a concrete example:

type Hello struct {}

func (h Hello) Greet() string {
    return "Hello world"
}

type Greeter interface {
    Greet() string
}

func ProvideGreeter() Greeter {
    return Hello{}
}

type App struct {
    Greeter Greeter
    Timer Timer
}

type RealTimer struct {}

func (r RealTimer) Now() string {
    return time.Now().String()
}

type Timer interface {
    Now() string
}

func ProvideTimer() Timer {
    return RealTimer{}
}

func NewApp(g Greeter, t Timer) App {
    return App{Greeter: g, Timer: t}
}

func (a App) GreetNow() string {
    return a.Timer.Now() + " " + a.Greeter.Greet()
}

Now let's say you have an injector like this that you normally use to construct your app:

func InitializeApp(ctx context.Context) (App, error) {
	wire.Build(ProviderGreeter, ProvideTimer)
	return App{}, nil
}

Now you want to mock out time in a test, but use an otherwise normal app:

type MockTimer struct {
    Time string
}

func (m MockTimer) Now() string {
    return m.Time
}

Ideas for how you might do that:

  • You can create a different initializer for your test, but if you do that, you can't dynamically configure the mock based on ex. a table driven test. If you end up using globals to configure the mock, you might also run into issues with concurrent tests.
  • You can use the real initializer, create a full App, then reach in and swap out the implementation of the Timer before calling any methods on the App
  • You can use the real initializer to create a full App, then reach in and use the Greeter and your own Timer to create a new App. This is easy only because App is the top level of the tree, but if it's deeply nested, you'd have to rebuild everything above it manually.

Is there a better way to do mocking? Am I missing another good option?

wire: create injectors in _test files

Currently, Wire only considers the non-test files for injectors. To enable creation of test-only injectors, it would be nice to have Wire evaluate test files as well, creating a separate wire_gen_test.go file for the generated code evaluated for the tests.

wire: separate go.mod to avoid pulling all of go-cloud?

I'm using wire in a few projects and loving it, thanks!

That said, it would be awesome if using wire didn't require me to pull in all of go-cloud. Have you considering including a separate, nested go.mod or extracting the project to its own repository?

Thanks again!

wire: ignore type-check errors in functions that can't be injectors

Another DevEx slowdown when using Wire is that it requires all the functions in the injector package to type-check before it will run. It would be nice to have it combine this with a quick AST pattern match to see if the function could even plausibly be an injector function before aborting the process.

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.