dmitrytsepelev / rubocop-graphql Goto Github PK
View Code? Open in Web Editor NEWRubocop extension for enforcing graphql-ruby best practices
License: MIT License
Rubocop extension for enforcing graphql-ruby best practices
License: MIT License
# frozen_string_literal: true
module Types
module SomeInterfaceType
include Types::BaseInterface
field :field_one, String, null: false
field :field_two, String, null: true
def field_one
# do something
end
def field_two
# do something else
end
end
end
with
GraphQL/FieldDefinitions:
Enabled: true
EnforcedStyle: define_resolver_after_definition
Produces no warnings or diffs, presumably because it's only looking for classes that inherit from GraphQL::Schema::Object
, and not modules?
cc @ravangen @toneymathews I think you've been looking at this cop recently, not sure if you have any insight? Also, hi, hope everything is good!
cc @exterm
For graphql-ruby >= 1.13
(changelog), a schema may now contain multiple members with the same name.
example_input.rb:13:9: C: [Corrected] GraphQL/FieldDefinitions: Define resolver method after field definition.
field( ...
^^^^^^
example_input.rb:29:9: C: [Corrected] GraphQL/FieldDefinitions: Define resolver method after field definition.
field( ...
^^^^^^
Then it actually changes the order in the code, and move the second field definition under the code method, and if I run it again it does the reverse. Rubocop seems to be confused by the fact that the field is declared twice.
We have just started adopting GraphQL Enterprise Changesets and it seems to conflict with this cop:
class Changesets::FixIdFieldTypes < GraphQL::Enterprise::Changeset
release "2022-05-16"
modifies Types::Objects::ExampleType do
field :id, ID, null: false
end
modifies Types::Objects::OtherExampleType do
field :id, ID, null: false
end
end
raises
app/graphql/changesets/fix_id_field_types.rb:9:5: C: GraphQL/FieldUniqueness: Field names should only be defined once per type. Field id is duplicated.
field :id, ID, null: false
It appears to think this is the same file defining the same field twice in a row.
Hi and thanks for the excellent package! 👏🏼
We are currently working on breaking down our Rails monolith with some tools from the packs ecosystem. This led us to moving all our GraphQL code to a folder packages/web_graphql
.
This clashes with the default Include
and Exclude
rules in this package, which often hardcode a graphql
directory. Example:
rubocop-graphql/config/default.yml
Lines 3 to 4 in ca4506b
If this was the only rule, then I wouldn't worry about it so much, and we did override it in our Rubocop config for now. But since there are several other, more specific rules also using similar constructs, I'm worried about quietly and unintentionally skipping a lot of valuable cops.
Would you be open to changing this to *graphql*
to make it work with any directory containing the term "graphql"?
Hey there!
Thanks so much for publishing this gem! We've been using it in our production app in the last while and it's been great! One issue that we found though is with the GraphQL/ObjectDescription
rule. This seems to flag any class within the graphql folder—sometimes erroneously.
For example:
class OrderStatusSync < Mutations::BaseMutation
graphql_name "PatientOrderStatusSync"
description "Fetch an updated tracking status for the given order"
class TrackingInfoNotAvailable < StandardError; end
end
In this case the TrackingInfoNotAvailable class gets flagged as violating the rule and it needs a description (which of course, StandardError does not provide).
Just wondering what your thoughts are. Would a simple return unless klass.respond_to?(:description)
be enough here? Happy to open a PR if you think that's a good idea. Thanks!
Recently I ran into a cyclical load error with resolvers. After searching for a solution, I found that this it a known issue and could be resolved by an easy change described here: https://graphql-ruby.org/fields/resolvers.html#nesting-resolvers-of-the-same-type
Then it popped to my mind why we don't warn people about this potential issue with rubocop? If it sounds reasonable to you, I'd like to write a new cop suggesting that you should have a string type definition in resolvers.
# Resovers should use strings for type definitions. See: https://graphql-ruby.org/fields/resolvers.html#nesting-resolvers-of-the-same-type
#
# @example
# # good
#
# module Resolvers
# class TasksResolver < GraphQL::Schema::Resolver
# type '[Types::TaskType]', null: false
#
# def resolve
# []
# end
# end
# end
#
# # bad
#
# module Resolvers
# class TasksResolver < GraphQL::Schema::Resolver
# type [Types::TaskType], null: false
#
# def resolve
# []
# end
# end
# end
The QueryContext < GraphQL::Query::Context
class located in app/graphql/query_context.rb
should be ignored by default against the GraphQL/ObjectDescription
rule.
This is a semi-duplicate of #110 because that was closed and I do not have permissions to re-open it.
The fix for that issue did fix modules when the style is group_definitions
, but our rubocop config uses the style define_resolver_after_definition
, which still doesn't run on modules.
# frozen_string_literal: true
module Types
module SomeInterfaceType
include Types::BaseInterface
field :field_one, String, null: false
field :field_two, String, null: true
def field_one
# do something
end
def field_two
# do something else
end
end
end
with
GraphQL/FieldDefinitions:
Enabled: true
EnforcedStyle: define_resolver_after_definition
Produces no warnings or diffs. (Note the EnforcedStyle
).
cc @exterm again
We're getting rubocop offenses when nested classes have the same field names. However, these should not be seen as duplicates.
We weren't getting this false result until today. If we can track down what might have changed, I'll report back.
Is anyone else getting the same result?
module Api
module VGraphQL
module Types
class OrderType < Types::BaseObject
class AddonType < Types::BaseObject
field :quantity, Int, null: false
end
class LineItemType < Types::BaseObject
field :quantity, Int, null: false
end
field :addons, [AddonType], null: true
field :line_items, [LineItemType], null: false
end
end
end
end
Hi,
The ExtractInputType: include only mutations fix only works if the graphql folder is in the top directory.
I'd imagine many place their graphql folder in the rails app directory, leading to this recommendation not occurring.
I'd opt for placing a wild card before the below include so the graphql folder is picked up whether its nested or not.
config/default.yml:76
Include:
- 'graphql/mutations/**/*.rb'
Include:
- '**/graphql/mutations/**/*.rb'
I get the following cop error when inspecting a type with an argument inside a field:
An error occurred while GraphQL/ArgumentUniqueness cop was inspecting test_type.rb:1:0
The following is a very simplified type that can trigger the error:
class TestType < ::GraphQL::Schema::Object
field name: :test, type: Boolean do
argument :arg, Boolean
end
end
The following definition results in the correctable offense GraphQL/FieldHashKey: Use hash_key: 0
:
class ExampleType
field :example, null: false
def example
object[0]
end
end
Changing this to
field :example, null: false, hash_key: 0
as suggested doesn't work, leading instead to a NoMethodError: undefined method 'to_sym' for 0:Integer
.
I'm not sure if there's a better way to implement this that the cop could suggest instead, or if it'd be better for it just not to trigger in this case.
GraphQL/Ordered*
cops autocorrection doesn't work correctly in classes with heredoc.
module Resolvers
class Test < Resolvers::Base
argument :test_2, Int
argument :test_1, Int,
description: <<~DESC
heredoc example
DESC
end
end
module Resolvers
class Test < Resolvers::Base
argument :test_1, Int,
description: <<~DESC
argument :test_2, Int
heredoc example
DESC
end
end
module Resolvers
class Test < Resolvers::Base
argument :test_1, Int,
description: <<~DESC
heredoc example
DESC
argument :test_2, Int
end
end
Hi guys, i have met the issue with updating of base rubocop version from 0.88 -> 0.89
File with module RuboCop::Cop::TooManyLines has been removed in this commit
All the places with this constant usage have been replaced with Rubocop::Cop::CodeLength.
Also module CodeLength
has been patched to handle both of cases - previous behaviour of CodeLength module and TooManyLines functionality
RuboCop is running successfully
Error is raised during rubocop starting
uninitialized constant RuboCop::Cop::TooManyLines
/Users/my_user/.rvm/gems/ruby-2.6.5@my_project/gems/rubocop-graphql-0.4.0/lib/rubocop/cop/graphql/resolver_method_length.rb:12:in `<class:ResolverMethodLength>'
Run the rubocop v0.89.0 with included rubocop-graphql gem
0.89.0 (using Parser 2.5.1.2, running on ruby 2.6.5 x86_64-linux)
resolver_method_length
Copresolver_method_length
Cop with check the version of rubocop and including correct constant(this check could be done at the start of rubocop running - not in this file)The unnecessary_argument_camelize rule doc says:
# # good
#
# class UserType < BaseType
# argument :filter, String, required: false, camelize: false
# end
#
# # bad
#
# class UserType < BaseType
# argument :filter, String, required: false
# end
It should be the other way around right?
Unless I'm missing something
I can fix it if it's the case
Hi!
I use a constant as a description value as I try to be more DRY in case I have the same description for a type, field and argument. But GraphQL/ObjectDescription complains when using a constant like this:
class SomeType < BaseType
description DESCRIPTION_CONSTANT
end
The default graphql-ruby approach to unauthorized objects is to replace them with null. In order for that to work, the field declarations referring to these types must be null: true
Do you think it is possible to create a cop to make sure that all fields that reference types have null: true?
Hi, thank you for your work 👍
I've just updated to 1.0 and got some offences with new cops.
I've read the docs and added query analyzers for my schema class. After deciding on my max complexity and max depth, I've set them on my schema class but the offences persist for the analyzer classes.
class ComplexityAnalyzer < GraphQL::Analysis::AST::QueryComplexity
def result
query.context[:_complexity] = super
end
end
class DepthAnalyzer < GraphQL::Analysis::AST::QueryDepth
def result
query.context[:_depth] = super
end
end
class MySchema < GraphQL::Schema
query_analyzer ComplexityAnalyzer
query_analyzer DepthAnalyzer
mutation(Types::MutationType)
query(Types::QueryType)
max_complexity 100
max_depth 10
end
It seems that the new cop fails if the class method authorized?
is defined inside a class << self
block.
I have created this PR with a failing test that I think should pass.
Thanks!
Heya, thank you for making this gem.
We have some custom error classes defined locally within our gql schema, and these lints are popping for those classes as well.
class MySchema < GraphQL::Schema
max_complexity 100
max_depth 10
class AccountDisabledError < GraphQL::ExecutionError; end # still complains about this, even though they're set in the schema
end
Bad:
class Types::UserType < Types::BaseObject
field :registeredAt, String, null: false
end
Good:
class Types::UserType < Types::BaseObject
field :registered_at, String, null: false
end
Bad:
class BanUser < BaseMutation
argument :uuid, ID, required: true
end
Good:
class BanUser < BaseMutation
argument :uuid, ID, required: true, description: "UUID of the user to ban"
end
Check out OrderFields PR as an example
The rules for filling out GraphQL descriptions are a bit annoying, because they make you do monotonous work. I was thinking that maybe we could set a file with default values for specific characters for the cop to use for autocorrect? What I mean: we have some field without description:
field :company_id, Types::CompanyType
And some file:
descriptions:
fields:
company_id: ID of company
I run:
bin/rubocop -A
And it autocorrects it to:
field :company_id, Types::CompanyType, description: 'ID of company.'
Can this be accomplished with a rubocop?
the linter is complaining about NotAuthorizedNodeType
even when we use the pundit integration. it should see that we are doing the authorization through pundit and so be silent for the type doing authorization through pundit.
module Types
class UserType < Types::Base::Object
description "User personal data information"
implements GraphQL::Types::Relay::Node
global_id_field :id
pundit_policy_class UserPolicy
pundit_role :show
end
end
Hi there, I am just getting started with this library, and definitely open to contributing this myself, but first thought I would see what you thought of the idea.
I think, sometimes, things do not need documentation. I would say the field User.firstName
, the object Query
, and the argument Query.fetchAccount(id: ID!)
all gain little to nothing from being described.
I have found that the argument cop is appeased with a argument :id, ..., description: nil
, but not description: false
. Neither seems to work for object descriptions.
What do you think of allowing explicit opt out on a case by case basis via:
class QueryType
description false
field :account, AccountType, description: false do
argument :id, ID, description: false, required: true
end
end
This is similar to how Elixir's Hexdocs handles module docs, and is a fair bit more concise than #rubocop:disable GraphQL/XXXXError
all over the place
I have tested and GraphQL Ruby appears to be fine with these false values
I am getting GraphQL/ObjectDescription: Missing type description
from those Unit Testing class, located under test/graphql
folder for testing graphql related classes.
Currently, I am using the following setting in .rubocop.yml
to bypass the linter check:
GraphQL/ObjectDescription:
Exclude:
- test/graphql/types/*.rb
- test/graphql/*.rb
I would like to know is there a better way to address this? Is there a way to add object description to unit test class?
In the following case, if max_depth and max_complexity are defined in a subclass (or even in an include of a subclass), offenses will be detected for the corresponding cops.
Using Classic Autoloader.
Thanks in advance
class GraphqlSchema < Graphql::BaseSchema
...
end
module Graphql
class BaseSchema < ::GraphQL::Schema
include ::Graphql::Concerns::BaseSchema
end
end
module Graphql
module Concerns
module BaseSchema
extend ActiveSupport::Concern
included do
max_depth(...)
max_complexity(...)
default_max_page_size(...)
end
end
end
end
Hey guys not sure if this is still maintained, for some reason running rubocop -a is throwing several
GraphQL/ExtractInputType errors inside of an input type object
example of the error:
app/graphql/types/public/program_application_attributes.rb:11:7: C: GraphQL/ExtractInputType: Consider moving arguments to a new input type
argument :company_size, String, required: false, description: 'size of company ex. 11-50'
example of the file structure and class definition
# app/graphql/types/public/program_application_attributes.rb
module Types
module Public
class ProgramApplicationAttributes < Types::BaseInputObject
argument :company_size, String, required: false, description: 'size of company ex. 11-50'
do you guy have any idea how to fix the error without disabling the feature?
When arguments added to a field like below:
field :my_field, String, null: false do
argument :my_options, [MyOption]
end
def resolve
::AcceptableData::CreateCommand.call
end
rubocop-graphql recognises the GraphQL/UnusedArgument
offense:
Offenses:
app/graphql/mutations/create_my_data.rb:22:5: C: [Correctable] GraphQL/UnusedArgument: Argument my_options: should be listed in the resolve signature.
def resolve ...
^^^^^^^^^^^
For implementations that use a more generic approach defined in a base class authorized?
, this new cop fails. Any objection to loosening this up a bit to allow for base class implementation?
Original PR: #115
Hello,
rubocop-graphql should respect ruby ...
(shorthand syntax for forwarding arguments to a method).
Example code:
def resolve(...)
Forms::UpdateUserForm.new(context, ...)
end
class Mutations::DoTheThng < Mutations::BaseMutation
argument :foo, String, required: true
argument :bar, String, required: true
argument :baz, Boolean, required: false
def resolve(foo:, bar:, baz: false)
# ...
end
end
Code such as the above produces the error:
GraphQL/UnusedArgument: Argument remember_me: should be listed in the resolve signature.
Using the auto-correct feature leads to the resolve method being changed to:
def resolve(foo:, bar:, baz: false, baz:)
Arguments should either be listed explicitly or **rest
should be in the resolve
signature
# good
class SomeResolver < Resolvers::Base
type SomeType, null: false
argument :arg1, String, required: true
argument :arg2, String, required: true
def resolve(arg1:, arg2:); end
end
class SomeResolver < Resolvers::Base
type SomeType, null: false
argument :arg1, String, required: true
argument :arg2, String, required: true
def resolve(arg1:, **rest); end
end
class SomeResolver < Resolvers::Base
type SomeType, null: false
argument :arg1, String, required: true
argument :arg2, String, required: true
def resolve(args); end
end
# bad
class SomeResolver < Resolvers::Base
type SomeType, null: false
argument :arg1, String, required: true
argument :arg2, String, required: true
def resolve(arg1:); end
end
Good:
class Types::UserType < Types::BaseObject
field :phone, String, null: true, hash_key: :home_phone
end
Bad:
class Types::UserType < Types::BaseObject
field :phone, String, null: true
def phone
object[:home_phone]
end
end
When a field definition gets too long I use the method form of the description parameter, like so:
field :update_user, Types::UserType, null: true, mutation: Mutation::UserMutation do
description 'Update user'
argument :id, Integer, required: true, description: 'User ID'
end
But this triggers the following error:
RuboCop: Missing field description [GraphQL/FieldDescription]
I know you have a find_description_method(@nodes)
that can be used inside the cop to check whether it's set as a method, but I am not competent enough with Rubocop for a PR.
For mutation classes, you can specify loads:
and as:
options to its arguments to automatically load an object by its ID, and then alias the argument name sent to the resolve
method:
https://graphql-ruby.org/mutations/mutation_classes#auto-loading-arguments
Example from the docs:
class Mutations::AddStar < Mutations::BaseMutation
argument :post_id, ID, loads: Types::Post, as: :something
field :post, Types::Post
def resolve(something:)
something.star
{
post: something
}
end
end
Currently this example results in an incorrect GraphQL/UnusedArgument: Argument post_id: should be listed in the resolve signature
rule violation.
The same error also occurs when as
is not specified, and the default auto-loaded post
argument is used.
Example from the docs:
class Mutations::AddStar < Mutations::BaseMutation
argument :post_id, ID, loads: Types::Post
field :post, Types::Post
def resolve(post:)
post.star
{
post: post,
}
end
end
Good:
class Types::UserType < Types::BaseObject
description "Represents application user"
end
Thanks for this gem! Would you accept a PR that adds autocorrect to UnnecessaryFieldAlias
cop?
running rubocop on the tests fails like this
test/graphql/types/event_show_type_test.rb:6:9: C: GraphQL/ObjectDescription: Missing type description
class EventShowTypeTest < ActionDispatch::IntegrationTest
^^^^^^^^^^^^^^^^^
i think updating the default.yml in the project may make sense
class SomeType < Types::BaseObject
field :id, Integer
field :some_field, Integer
field :field_too, Integer
field :field, Integer
end
leads to
$ bundle exec rubocop --only GraphQL/OrderedFields -ad app/graphql/types/some_type.rb
…
An error occurred while GraphQL/OrderedFields cop was inspecting /…/app/graphql/types/some_type.rb:1:0.
Parser::Source::TreeRewriter detected clobbering
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter.rb:427:in `trigger_policy'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter.rb:414:in `enforce_policy'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:225:in `call'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:225:in `call_enforcer_for_merge'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:216:in `merge'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:104:in `do_combine'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:117:in `place_in_hierarchy'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:131:in `block in combine_children'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:130:in `each'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:130:in `inject'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:130:in `combine_children'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:221:in `merge'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:104:in `do_combine'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter/action.rb:30:in `combine'
…/gems/parser-3.0.3.2/lib/parser/source/tree_rewriter.rb:143:in `merge!'
…/gems/rubocop-1.24.0/lib/rubocop/cop/base.rb:291:in `apply_correction'
…/gems/rubocop-1.24.0/lib/rubocop/cop/base.rb:375:in `attempt_correction'
…/gems/rubocop-1.24.0/lib/rubocop/cop/base.rb:356:in `use_corrector'
…/gems/rubocop-1.24.0/lib/rubocop/cop/base.rb:350:in `correct'
…/gems/rubocop-1.24.0/lib/rubocop/cop/base.rb:127:in `add_offense'
…/gems/rubocop-graphql-0.12.0/lib/rubocop/cop/graphql/ordered_fields.rb:60:in `register_offense'
…/gems/rubocop-graphql-0.12.0/lib/rubocop/cop/graphql/ordered_fields.rb:47:in `block in on_class'
…/gems/rubocop-graphql-0.12.0/lib/rubocop/cop/graphql/ordered_fields.rb:100:in `block in field_declarations'
…/gems/rubocop-ast-1.15.1/lib/rubocop/ast/node/mixin/descendence.rb:106:in `block in visit_descendants'
…
on
$ bundle exec rubocop -V
1.24.0 (using Parser 3.0.3.2, rubocop-ast 1.15.1, running on ruby 3.1.0 x86_64-linux)
with rubocop-graphql-0.12.0
(not sure if it's rubocop or rubocop-graphql but that it's not showing in rubocop -V
).
Hello!
I ran into an issue with the Ordered Arguments cop and an argument containing a block. Below is an example of what I had:
argument :sandwich_count, Int, required: false
argument :orange_slice_count, Int, required: false do
description 'The number of oranges slices for the soccer game'
end
I got the expected Rubocop message:
E: 22: 7: [Correctable] GraphQL/OrderedArguments: Arguments should be sorted in an alphabetical order within their section. Field orange_slice_count should appear before sandwich_count.
Upon auto correction, I ended up with the following:
argument :orange_slice_count, Int, required: false do
argument :sandwich_count, Int, required: false
description 'The number of oranges slices for the soccer game'
end
But I think we should expect:
argument :orange_slice_count, Int, required: false do
description 'The number of oranges slices for the soccer game'
end
argument :sandwich_count, Int, required: false
I also tested this with the scenario of both arguments having blocks containing descriptions and this actually does not trigger the cop because they're not on consecutive lines.
Does not trigger cop
argument :sandwich_count, Int, required: false do
description 'The number of sandwiches to take to the face'
end
argument :orange_slice_count, Int, required: false do
description 'The number of oranges slices for the soccer game'
end
I will also say that I ran into this a few months ago when we didn't require mutation arguments to have descriptions — the dark ages —, but now we're on the straight and narrow so this won't be an issue I'll see anymore, but wanted to flag it.
Suggest using input type when mutation has more that two arguments
I've tried adding multiline comments in the following formats:
class PersonType < BaseObject
description "A Person has four limbs.
People can ride buses and bikes."
and
class PersonType < BaseObject
description "A Person has four limbs."\
"People can ride buses and bikes."
Both are not picked up by the cop as compliant. I've seen it on Objects and Arguments thus far. Can this be supported with some documentation on which way might be preferable? Thanks! CC @bessey
Good:
class Types::UserType < Types::BaseObject
field :phone, String, null: true, method: :home_phone
end
Bad:
class Types::UserType < Types::BaseObject
field :phone, String, null: true
def phone
object.home_phone
end
end
id
is of course a special field, and our codebase has a convention to place the id
field first above all other fields.
Would you accept a configuration such as IdFieldFirst: True/False
to enforce + autocorrect id to be first "alphabetically"?
OrderedArguments
could also support the same option.
Thanks!
Suggest extracting type when there are more than two fields with the same prefix.
Bad:
class Types::UserType < Types::BaseObject
field :registered_at, String, null: false
field :contact_phone, String, null: false
field :contact_first_name, String, null: false
field :contact_last_name, String, null: false
end
Good:
class Types::ContactType < Types::BaseObject
field :phone, String, null: false
field :first_name, String, null: false
field :last_name, String, null: false
end
class Types::UserType < Types::BaseObject
field :registered_at, String, null: false
field :contact, Types::ContactType, null: false
end
Currently if there is a duplicate argument defined, it causes an infinite loop when processing GraphQL/OrderedArguments
, and the same for fields and GraphQL/OrderedFields
.
Obviously this is a problem of its own in our schema and we were able to find the duplicates pretty quickly, but it likely shouldn't cause an infinite loop.
Perhaps duplicated field/argument definitions could be their own cops.
Similar to UnnecessaryFieldAlias
cop, it would be good to have a cop to detect unnecessary method
args like:
field :type, String, null: false, method: :type
And similarly for hash_key
.
Given the following type:
class Types::AType < BaseObject
field :name, String, null: false, method: :method_override
def name
object.another_method
end
end
this gem reports that we should set method
to be :another_method
and the autocorrector will change this to the following:
field :name, String, null: false, method: :method_override, method: :another_method
I feel the GraphQL/FieldMethod shouldn't trigger if the method
key is already set.
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.