Giter Site home page Giter Site logo

mage2gen's Introduction

image

Mage2Gen

Mage2Gen is a python library for generating Magento 2 modules. It is build to be extendable with snippets for creating more complex Magento 2 modules based on simple input.

Installation

To install the Python library and the command line utility, run:

sudo pip3 install mage2gen

Interactive command line

With the mage2gen command line tool you can interactively create and generate Magento 2 modules. If you have installed mage2gen for your whole system you can start it by running mage2gen. You will be asked to give the module a package name, name and description:

bash> mage2gen
Package name [Mage2gen]: Mage2gen
Module name: Test
Description: A Mage2Gen test module

Type help or ? to list commands.
(Mage2Gen) 

Help

You can use help or ? to show all available commands and help <command> to show command specific help descriptions:

(Mage2Gen) help

Documented commands (type help <topic>):
========================================
add  exit  generate  help  info  list  remove

Undocumented commands:
======================
EOF

(Mage2Gen) help add
Add a snippet to the module

List snippets

With the list command you get a list of all the available snippets you can add to your module:

(Mage2Gen) list
console cronjob controller system plugin shipping install language observer payment

Add snippet

To add a snippet you can use the add <snippet name> command, you can auto-complete a snippet name with TAB:

(Mage2Gen) add console
Action name*: test
Short description*: Test log command

Show added snippets

When you have added multiple snippets and you want to see which snippets are added to the module you can use the info command to show an overview:

(Mage2Gen) info

Mage2gen/Test

Consoles

Index  Action name  Short description  
--------------------------------------------------------------------------------
0      test         Test log command   
--------------------------------------------------------------------------------

Remove snippet

When you want to remove an added snippet you can use the remove <snippet name> <index> command, to remove the snippet from the module:

(Mage2Gen) remove console 0
Removed Console snippet

Generate module

When you are ready with your module and added the snippets you want to use, you can generate the module with the generate command. If you are inside a Magento 2 project directory, it will select the default path for the module:

(Mage2Gen) generate
Generate path [/media/data/Downloads/magento2/app/code]*: 
Path does not exist, do you want to create it? [y/N]: y
Module (Mage2gen/Test) generated to: /media/data/Downloads/magento2/app/code

Example usage library

from mage2gen import Module

# Create a module (Module1) for the package (Mage2gen)
module = Module('Mage2gen', 'Module1')

# Generate module files to folder (to_folder)
module.generate_module('to_folder')

Snippets

Mage2Gen has core classes for creating and merging PHP classes, XML files and static files. For generating a module you don't want to define your PHP class or XML file for basic module concepts like observers, plugins or controllers. This is where snippets come in, which add these concepts based on simple input. The currently supported snippets are listed below. If you would like to add a snippet to Mage2Gen, simply fork this project. Add your snippet or other improvements and create a pull request afterwards.

Controller

Creates a controller with block, layout.xml and template. Can create a controller for frontend and adminhtml.

Params:

  • (str) frontname: frontame route for module
  • (str) section: subfolder in module/Controller
  • (str) action: action class
  • (bool) adminhtml [False]: if controller is used for adminhtml

Example:

from mage2gen.snippets import ControllerSnippet

controller_snippet = ControllerSnippet(module)
controller_snippet.add(frontname='mage2gen', section='order', action='json')

Plugin

Creates a plugin for a public method, link to Magento 2 docs

Params:

  • (str) classname: full class namespace of class with method
  • (str) methodname: method name of class
  • (str) plugintype: type for plugin (before, after or around)
  • (bool) sortorder [10]: the order the plugin is executed in respect to other plugins.
  • (bool) disabled [False]: disable a plugin

Example:

from mage2gen.snippets import PluginSnippet

plugin_snippet = PluginSnippet(module)
plugin_snippet.add('Magento\Catalog\Model\Product', 'getName')

Observer

Create an observer for an event

Params:

  • (str) event: event name
  • (int) scope [ObserverSnippet.SCOPE_ALL]: handle observer for all (SCOPE_ALL), frontend (SCOPE_FRONTEND) or backend (SCOPE_ADMINHTML)

Example:

from mage2gen.snippets import ObserverSnippet

observer_snippet = ObserverSnippet(module)
observer_snippet.add('catalog_product_save_after')

Create a Snippet

You can create your own snippets. If you would like to add a snippet to Mage2Gen, simply fork this project. Add you snippet or other improvements and create a pull request afterwards. You can read this blog post for an how to guide on creating a snippet.

Base snippet

from mage2gen import Module, Phpclass, Phpmethod, Xmlnode, StaticFile, Snippet

class CustomSnippet(Snippet):
    def add(self, **params):
        # create and add PHP classes, XML and static files to the module

        # Get module name (<package>_<module>)
        self.module_name

        # Add PHP class to module (You can add the same class with different 
        # methods and attributes multiple times, Mage2Gen will merge them to 
        # one class with all the methods and attributes).
        self.add_class(PhpClassObject)

        # Add XML to module (Same as with the PHP class, you can add multiple
        # XML nodes for the same file !important root node must be the same.
        # An XML node will be merged when the node name and the XML attributes 
        # name or id  are the same. When creating a node you can define which
        # attributes make the node unique, default is name and id).
        self.add_xml('full/path/to/xml/with/file/name', XmlNodeObject)

        # Add static file
        self.add_static_file('path/to/file/location', StaticFileObject)

Adding a PHP class

TODO

Adding XML file

TODO

Adding Static file

TODO

TODO

  • Increase test coverage.
  • Adding more snippets:
    • Model attributes
    • Custom models with adminhtml grid
    • Adding fields to checkout process

Example implementation:

  • Interactive command line
  • Mage2gen Online Magento 2 Module Creator mage2gen

mage2gen's People

Contributors

and3k5 avatar basvanderlouw avatar blopa avatar borisvankatwijk avatar brunoroeder avatar casperf avatar collymore avatar dani97 avatar erikhansen avatar evanrijn avatar florisschreuder avatar heesbeen avatar krukas avatar lbajsarowicz avatar lewisvoncken avatar maikelexperius avatar marleenp1p5 avatar matthieu2607 avatar paugnu avatar prakashthapa avatar r-martins avatar rensieeee avatar rubenexp avatar shinesoftware avatar theuargb avatar tomvlk avatar tuyennn avatar vincentmarmiesse 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

mage2gen's Issues

System Snippet | Tab name has to be alphanumeric

Steps to reproduce

  1. Go to System Section
  2. Add a System Configuration
  3. Type "ImNotAsSmallAsYouThink" in the field "Tab"

Expected result

  1. There should be no Error or if necessary an Error Message which makes more sense
  2. Better Error Message would be "please use lowercase letters"

Actual result

  1. Error Message is "Only alphanumeric"
  2. http://recordit.co/eXeMYx0KoW

Please clarify the license of the generated code

Thanks for providing this code generator - after reading this and seeing that it might be arguable how much of the code (ie the templates) end up in the final generated output can you please clarify what the output is licensed under?

Docblocks

Files are now generated without any docblocks

Extension attributes plugins

I am just wondering, when adding a new customer attribute, don't we need to implement plugins for loading and saving the extension attribute? It seems that is missing in the generator.

payment.py

wrong tag for page: "pagepage" need replace to "page"

Magento Compilation fails

Hi guys,

when I try to compile the code

./n98-magerun2.phar setup:di:compile

I get this error:

Extra parameters passed to parent construct: $coreRegistry.

The problem is this file:

/Controller/Adminhtml/Wiki/Save.php

Best Regards

Customer Attribute Custom Source model error

protected $_optionsData;

/**
 * Constructor
 *
 * @param array $options
 */
 * Constructor
 *
 * @param array $options
 */
public function __construct(array $options)

{
    
    $this->_optionsData = $options;
}

Where is the Grid Collection Object?

Hello,

I have discovered in my downloaded sample module that there is not any:

Mycompany\Mymodule\Model\ResourceModel\Domain\Grid\Collection

in the di.xml is declared but there is not any file in that directory.

thanks

System Snippet

# Group Label
It isn't possible to add a System Group Label

# Sort Order
No default is set for the Sort Order of Tabs, Sections, Groups and fields

Generated Interface

Between the comment/phpdoc and the function name is a new line.

Api/Data/{interface}

Payment Method Not Working

if you try create a payment method, it will not show up on checkout page
i found incorrect generated layout xml

you can check at checkout_index_index.xml file

Model Fields Class have limitations

Hi Krukas,
I have found a strange limitation when I try to create more than 20 model fields.
I have checked the code but I have not found any constant to 20 model fields.

Any idea?

selezione_065

thanks

Cannot instantiate interface

There seems to be an error with the Model generation.

While trying to get a list by using the Repository class:

Fatal error: Uncaught Error: Cannot instantiate interface Vendor\Module\Api\Data\ModelnameSearchResultsInterface in public_html/vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php on line 73

Short Trace:

Vendor\Modulename\Model\ModelnameRepository->getList( )
Vendor\Modulename\Api\Data\ModelnameSearchResultsInterfaceFactory->create( ) .../ModelnameRepository.php:109
Magento\Framework\ObjectManager\ObjectManager->create( ) .../ModelnameSearchResultsInterfaceFactory.php:43
Magento\Framework\ObjectManager\Factory\Dynamic\Developer->create( ) .../ObjectManager.php:57

Constant instead string name

I think that replace string name to constant will be correct for all entities like category, product, customer and order.

Example:
addAttribute('customer' => addAttribute(Customer::ENTITY with use Magento\Customer\Model\Customer;

$customerSetup->addAttribute('customer', '{attribute_code}', [
    'type' => '{value_type}',
    'label' => '{attribute_label}',
    'input' => '{frontend_input}',
    'source' => '{source_model}',
    'required' => {required},
    'visible' => {visible},
    'position' => {sort_order},
    'system' => false,
    'backend' => '{backend_model}'
]);

https://github.com/krukas/Mage2Gen/blob/master/mage2gen/templates/attributes/customerattribute.tmpl

Wrong title in model list page

When I click on the module custom link, Magento open the model page where the datagrid shown all the records. Into this page, the title has been not set correctly.

selezione_018

here the code

/**
  * Index action
  *
  * @return \Magento\Framework\Controller\ResultInterface
  */
 public function execute()
 {
     $resultPage = $this->resultPageFactory->create();
         $resultPage->getConfig()->getTitle()->prepend(__("'+model_name+'"));
         return $resultPage;
 }

thanks

Reorder models

When I make a new model I like to add attributes that have similar content close to each other. Now I cannot reorder the attributes in the model when I make additional attributes.

Copyright and package Information in each file

It would be great if there could be inserted a small copyright and package/method documentation like:
At the top file

/**
 * Some copyright text.
 *
 * @category    ***
 * @package     ***
 * @author      ****
 */

for the Method:

/**
 * Register user contexts.
     *
     * @param CompositeHelper $compositeHelper
     * @param UserContextInterface[] $userContexts
     * @return RETURN_TYPE
 */
 protected function add(UserContextInterface $userContext)
    {
        $this->userContexts[] = $userContext;
        return $this;
    }

source_model Class does not exist

So I tried to create a new module with one observer and few system settings. But something is wrong with system.xml I guess. Everything works expect custom source_model. If I look into system.log it says
Class Company\MyAwesomeModule\Model\Config\Source\My_awesome_clss does not exist.

So if I comment out the fields using custom source_model inside system.xml , the group does appear inside admin area under my tab->section. but it I uncomment nothing shows inside configuration. Although tab and section is there........I hope you get it what I am trying to say here.

Model field sorting feature

When I try to create a module sometimes I am focusing on the structure of the model (database) and I forget about the sorting of the fields. One of the best feature is the sorting of the fields in the model section in order to create a perfect form in Magento administration panel.

thanks

Collections & MassActions

Hello guys,

I have found a problem in the NAMESPACE\MODULE\Model\ResourceModel\ENTITY when we use the MassAction in the Admin Grid.

You have to add this line:

protected $_idFieldName = 'entity_id';

thanks

User frontend panel

Hello,

thanks for your interesting wizard for Magento 2! It is very helpful!
Is there a way to create a simple form based on the model for the users?

I need to create a link on the user panel that helps the users to add information.

thanks

Unable to place order. Please try again later.

hi, after created a customer address attribute i get this error: unable to place order. Please try again later. went i try to place order.

this it the code:

registration.php

<?php 
\Magento\Framework\Component\ComponentRegistrar::register(
   \Magento\Framework\Component\ComponentRegistrar::MODULE,
   'Some_IdNumberShipping',
   __DIR__
);

composer.json

{
    "name": "some/idnumbershipping",
    "description": "",
    "authors": [
        {
            "email": "mail@mage2gen",
            "name": "Mage2Gen"
        }
    ],
    "minimum-stability": "dev",
    "require": {},
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "Some\\IdNumberShipping\\": ""
        }
    }
}

InstallData.php

<?php 


namespace Some\IdNumberShipping\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Customer\Model\Customer;
use Magento\Framework\Setup\ModuleContextInterface; 

class InstallData implements InstallDataInterface {

    private $customerSetupFactory;

    public function __construct(
        CustomerSetupFactory $customerSetupFactory
    ){
        $this->customerSetupFactory = $customerSetupFactory;
    }

    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ){
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);

        $customerSetup->addAttribute('customer_address', 'id_shippingNumber', [
            'label' => 'Id de quien recibe',
            'input' => 'text',
            'type' => 'varchar',
            'source' => '',
            'required' => True,
            'position' => 1,
            'visible' => True,
            'system' => false,
            'is_used_in_grid' => false,
            'is_visible_in_grid' => false,
            'is_filterable_in_grid' => false,
            'is_searchable_in_grid' => false,
            'backend' => ''
        ]);

        $attribute = $customerSetup->getEavConfig()->getAttribute('customer_address', 'id_shippingNumber')->addData(['used_in_forms' => ['adminhtml_customer_address','customer_address_edit','customer_register_address']]);
        $attribute->save();
    }
}

InstallSchena.php

<?php 


namespace Some\IdNumberShipping\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface; 

class InstallSchema implements InstallSchemaInterface {


    public function install(
        SchemaSetupInterface $setup,
        ModuleContextInterface $context
    ){
        $installer = $setup;
        $installer->startSetup();

        $setup->endSetup();
    }
}

etc/module.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Some_IdNumberShipping" setup_version="1.0.0"/>
</config>

etc/extension_attributes.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Customer\Api\Data\AddressInterface">
        <attribute code="id_shippingNumber" type="string"/>
    </extension_attributes>
</config>

image

Model Snippet: Repository modul implement wrong

When i use a underscored model name "product_alert_stock" it implements a non excisting class.

class ProductAlertStockRepository implements product\alert\stockRepositoryInterface

should be

class ProductAlertStockRepository implements ProductAlertStockSearchResultsInterfaceFactory

Multiselect attribute backend_model

If you create a attribute that uses a multiselect the backend_model is missing, I tested this on products. Did not try if it works correctly on customers.

Attribute Code Validation

There is no length validation for attribute codes. An attribute code must not be more than 30 characters.

Model groups and database table name

Hi Krukas,

when you try to create more than one module, the Mage2Gen software creates the mysql tables using the setup class but there is a problem.

For instance: Take a look the Library project sample posted in the previous issue.

Just for testing:
We need to create a Library module and we need more than one data model. For instance:

The Module name: Library

The Models are:

  • Books
  • Category
  • Tags

So I have created my models in this way: https://mage2gen.com/load/e9a12f34-9313-4b73-a6dd-1e650cd2ce99

Now the setup install will create 3 tables:

  • mycompany_books
  • mycompany_category
  • mycompany_tags

Mage2Gen helps me to create all the models but the name of the classes and the name of the database tables could be create a bit of confusion when the project need more than 3 models.

So I have thought to group all the models using the project name:

  • Library_Books
  • Library_Category
  • Library_Tags

but this solution creates a problem in the Magento folder structure.

Any hint?

Plugin naming convention

Mage2gen uses the whole namespace as path too the plugin class, but Magento has a convention to use the following path Vendor\Module\Plugin\{PluginClass}Plugin

For example if you write a plugin for the following class:
Aheadworks\Blog\Model\ResourceModel\PostRepository Mage2Gen will generate the class on the following path Vendor\Module\Plugin\Aheadworks\Blog\Model\ResourceModel\PostRepository

But the Magento convention is Vendor\Module\Plugin\PostRepositoryPlugin

Listing Grid & Tabs

Hello Guys,

I am using your fantastic software as a scaffolder of my modules and it saves me a lot of time! So, thanks! I would like to suggest you to improve the generation of the grids/listings and tab inside the adminhtml form.

Grids

the grid/listing needs to be improved using as possibile the Magento features like these:

selezione_072

Filters

The filters are raw and the user data are ignored.
selezione_064

Forms

For instance, If I need to create an adminhtml module with two tabs I need to rewrite your module adding the block classes and layout files.

selezione_073

I have to create the Block classes like these one because the UIComponent is too young to support the tab system. I have not found any documentation about the tab and form nesting by the UIComponent.

selezione_074

Upgrading the database resource using the version number

Ok! I have created the module and now I have downloaded it from your site to my store but... I forgot to add a db field in my model... and now?
I have to go to your website to add my new db field. I have to download again the module and I have to install it in my store but... the new field has been not created in the database ... why?

Because the setup_version remains "1.0.0" and Magento doesn't upgrade the databse when I execute the command by the bash:

php -f bin/magento setup:upgrade

So I suggest you to save the version of the module when the user download the module and offer a way to maintain the module releases.

Regards

Date attribute backend type wrong

If you create a product attribute with type date, both the input type and backend_type are set to date. While the backend_type should be datetime. Cause there is not product_date table only a product_datetime table.

Columns which should be displayed on admin grid

Hello,

When creating a model, we can choose if we want the fields to be displayed on admin grid or not.
All columns with the selected option are added to the file view/adminhtml/ui_component/module_model_index.xml, which is ok.

Then, the actionsColumn is added, which is also ok.

But after this actionsColumn, the columns which were not selected to be displayed on the admin grid are added. This is causing an error and blocks the admin grid render. The fix is to remove all columns after the actionsColumn.

I would like to make a PR but in the model.py file, I see the actionsColumn part but I don't see how you handle the Adminhtml grid checkbox.

Thanks!

Custom multiselect Catalog Category Attribute Class does not exist.

I have app/code/A4G/B2B/Model/Category/Attribute/Source/A4g_gtfeauters.php:
`<?php
namespace A4G\B2B\Model\Category\Attribute\Source;

class A4g_gtfeauters extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
{

/**
 * @var array
 */
protected $_optionsData;

/**
 * Constructor
 *
 * @param array $options
 */
public function __construct(\Psr\Log\LoggerInterface $logger, array $options)
{
    $logger->addInfo('A4g_gtfeauters');
    $this->_optionsData = $options;
}

/**
 * getAllOptions
 *
 * @return array
 */
public function getAllOptions()
{
    if ($this->_options === null) {
        $this->_options = [
            ['value' => (string) 'CARP', 'label' => __('CARP')],
            ['value' => (string) 'CATFISH', 'label' => __('CATFISH')],
            ['value' => (string) 'COMPETITION', 'label' => __('COMPETITION')],
            ['value' => (string) 'FEEDER', 'label' => __('FEEDER')],
            ['value' => (string) 'FLY', 'label' => __('FLY')],
            ['value' => (string) 'ICE', 'label' => __('ICE')],
            ['value' => (string) 'MATCH', 'label' => __('MATCH')],
            ['value' => (string) 'SALTWATER', 'label' => __('SALTWATER')],
            ['value' => (string) 'SPINNIG', 'label' => __('SPINNIG')]
        ]
    }
    return $this->_options;
}

}
`
generated by Mage2Gen, but still Magento shows me error:

Exception #0 (ReflectionException): Class A4G\B2B\Model\Category\Attribute\Source\A4g_gtfeauters does not exist.

Can you tell me what can make this kind of error? Logger function doesn't give any efect, error in code as well.

Model Admin Grid

@dheesbeen

dit

public function __construct( \Magento\Framework\App\Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory ){ $this->resultPageFactory = $resultPageFactory; parent::__construct($context); }

Zou eigenlijk dit moeten zijn:

public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\View\Result\PageFactory $resultPageFactory ){ $this->resultPageFactory = $resultPageFactory; parent::__construct($context); }

Customer Attribute with Custom Multiple Select

Er mist een punt comma achter de

$this->_options = [ ['value' => (string) 'experius_payment_multi_invoice1', 'label' => __('experius_payment_multi_invoice1')], ['value' => (string) 'msp', 'label' => __('msp')] ]

Provide https urls for downloads

After clicking save and download you get a curl example like:
curl http://mage2gen.com/download/random-module-url.tar | tar xzC app/code

This should be
curl -L http://mage2gen.com/download/random-module-url.tar | tar xzC app/code

Because the url gets redirected.

While I was writing this I took a look at the Location header. The reason it redirects is because of https. The provided url redirects to a https version. It will be better to just change that ๐Ÿ˜‰

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.