Coder Social home page Coder Social logo

meilisearch / meilisearch-js-plugins Goto Github PK

View Code? Open in Web Editor NEW
453.0 453.0 54.0 12.4 MB

The search client to use Meilisearch with InstantSearch.

Home Page: https://www.meilisearch.com

License: MIT License

JavaScript 17.56% HTML 2.88% Vue 1.53% CSS 4.27% TypeScript 73.75%
angular client geosearch instantsearch js meilisearch react vue

meilisearch-js-plugins's Introduction

Instant-Meilisearch

Meilisearch Javascript plugins

Tests License Bors enabled

Meilisearch is an open-source search engine. Discover what Meilisearch is!

This repository contains clients and plugins to use Meilisearch with third party tools.

Table of Contents

📖 Documentation

For general information on how to use Meilisearch—such as our API reference, tutorials, guides, and in-depth articles—refer to our main documentation website.

⚡ Supercharge your Meilisearch experience

Say goodbye to server deployment and manual updates with Meilisearch Cloud. Get started with a 14-day free trial! No credit card required.

🐱 Plugins

Plugins in this repository

instant-meilisearch The search client for instantsearch.js.
autocomplete-client The search client for autocomplete.

Other Plugins

strapi-plugin-meilisearch The plugin to add and synchronize your Strapi collections on Meilisearch.
firestore-meilisearch The plugin to synchronize your firestore data on Meilisearch.
gatsby-plugin-meilisearch The plugin to index your Gatsby content to Meilisearch.
docs-searchbar.js A search bar integration for all kinds of documentation.
vuepress-plugin-meilisearch The plugin to use docs-searchbar.js with vuepress.

⚙️ Development Workflow and Contributing

Any new contribution is more than welcome in this project!

If you want to know more about the development workflow or want to contribute to the plugins in this repository, please visit our contributing guidelines for detailed instructions!


Meilisearch provides and maintains many SDKs and Integration tools like this one. We want to provide everyone with an amazing search experience for any kind of project. If you want to contribute, make suggestions, or just know what's going on right now, visit us in the integration-guides repository.

meilisearch-js-plugins's People

Contributors

a11rew avatar andrelillvede avatar atoulmet avatar bb avatar bidoubiwa avatar bors[bot] avatar brunoocasali avatar carofg avatar curquiza avatar daniel-shuy avatar dependabot-preview[bot] avatar dependabot[bot] avatar dichotommy avatar fadeaway avatar fitimvata avatar flevi29 avatar github-actions[bot] avatar keer4n avatar krosenk729 avatar maryamsulemani97 avatar mdubus avatar meili-bors[bot] avatar meili-bot avatar papaioprog avatar polydevil avatar saintmalik avatar sc-kknowles avatar strift avatar tungbq 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

meilisearch-js-plugins's Issues

Restructure files to enable easy testing

Previously functions were all inside instantmeiliSearch which would have made integration testing complicated as we would only be able to do e2e.

Methods should be separated and imported so that we can import each individual function into a test file.

Make transform function available to the package user

Explaination

InstantMeiliSearch under the hood simply transforms a MeiliSearch search response into a search response structure instantSearch.js needs.
InstantSearch is the library we use that creates all UI components.

If we go even further, we realise that this search response instantSearch.js needs is also the search response algoliaClient provides on search.
Which makes instantSearch.js very compatible with algolia search client.

Suggestion

In another context, users that want to migrate from Algolia to MeiliSearch needs the same function instant-meilisearch is using to transform the responses.

By exporting these function we make migrating easy from one to another, not only when using instantSearch but also when using only algolia search client.

Changes after the Node.js v10 EOL (2021-04-30)

Related meilisearch/meilisearch-js#760

Node 10 is the end of life since April 2021. It becomes more and more unsupported by the libraries we use in our different JS project. Until now we had to make a workaround to keep compatibility with node 10.

As for the official EOL of node, we should also drop support for node 10 and stay up-to-date with the libraries that upgraded without node 10 support.

We need to:

  • Remove the tests with node.js v10 in the CIs
  • Upgrade jest-environment-jsdom to its latest version as it is blocked to the latest version that supported node 10

Errors not caught

When using instant meilisearch, if the host or the API key is wrong or the index doesn't exist, the only feedback is an "Uncaught (in promise) Error: Unhandled error. (undefined)" in the console. Those kinds of errors should be caught.

`isSearchStalled` causes an infinite loop with slow connections

Using the isSearchStalled props received from connectStateResults with a slow internet connection causes the page to re-render endlessly 🥲

Capture d’écran 2021-04-20 à 14 33 33

You can try it by adding the following code to the playground :

const Results = connectStateResults(({  isSearchStalled }) => {
  if (isSearchStalled) return <div>loading...</div>

  return <InfiniteHits hitComponent={Hit} />
})

Replace then the InfiniteHits component with the Results one. Don't forget to test with fast 3G or slower !

Filters are ignored

When filters are added to instantsearch.configure they are not propagated to meilisearch.

so doing

<Configure filters={`categories = "test"`} hitsPerPage={10} />

when using react is not filtering the results.

Missing searchable in refinements-list

When trying to use the searchable option in the ais-refinement-list it throws an error because the following method does not exist in instant-meilisearch:

vue.runtime.esm.js?2b0e:1888 Error: search for facet values (searchable) was called, but this client does not have a function client.searchForFacetValues or client.initIndex(index).searchForFacetValues

Here is the documentation about searchable in refinement:
https://www.algolia.com/doc/api-reference/widgets/refinement-list/vue/#widget-param-searchable

Add cache for frontend integrations

In order to have a performance gain, and also to have less server congestion. It is often recommended to put a cache on services. Caching of search requests can be done on MeiliSearch in several places. On the server directly with a service such as Memcached or Redis, using CDNs such as Cloudflare or Akamai, or directly on the requesting client.

It is this last point that I would like to emphasize in this issue. I would like to propose the idea of adding a local cache in memory for Javascript. This one will have to hide search results in the local cache (RAM). It is possible to use in javascript window.sessionStorage to do this. With as key, a hash of the request made and as value the answer. It might be recommended in the future to propose in the API a method to empty the cache completely.

Improve .gitignore by generating

Git .gitignore files are most of the time generated based on the environment in which you are working. Our gitignore is still made from scratch and thus a lot of awareness must be given to not adding files we do not want in this repo.

Using tools like this one: https://github.com/generate/generate-gitignore, we can easily improve our .gitignore with all common file names that are not interesting in a js/node env.

Dependabot desactivated until better testing

To avoid bumping new versions of packages without the knowledge of a possible breaking or bug, we prefer not updating any dependency until better tests are added. We deactivated Dependabot until we introduce better testing.

sort the facets by value

For the moment, the facets results (from facetsDistribution) are not consistently sorted, we should sort them by alphanumerical order before sending them to the front-end library.

Instant Search Pagination Not Working

Instant search pagination is not working, it just returns undefined on console which means there is no data after clicking the next page, I have tried this demo also looks like is not working either does any one know why is this happening or is it because of nbHits changes and how can I fix this?

Remove jest-puppeteer and replace it with jest and puppeteer separately

Introduction

To test if instant-meilisearch correctly works on browsers we launch a test with jest-puppeteer. This test creates a headless-chrome on which it runs instant-meilisearch. It then checks if the page content contains an instantMeiliSearch object.
This is a very very basic test, more should be added, but meanwhile, it ensures us that the library is included properly on a browser.

The test can be found in examples/express/tests/basic.test.js

Problem

jest-puppeteer, the library used to make the test has two problems:

  1. It is not maintained at the moment (see README on the repository)
  2. I can not catch errors from the browser. If instant-meilisearch fails on the browser it will always return a timeoutError. Which makes debugging annoying.

Solution

Jest, in its documentation, also showcases how to use the puppeteer library by itself and not jest-puppeteer to create tests (See this part of the jest documentation documentation).

The MVP of this issue would be to replace jest-puppeteer entirely and replace it with puppeteer the way jest shows how to do on their documentation.

Usage with TypeScript

I'd like to use this library with TypeScript, however there do not seem to be official typings available.

Are there any plans to add typings?
If so, which would be the preferred way?
One could:

  • convert the project to TypeScript (as e.g. meilisearch-js
    already is in TypeScript as well),
  • add separate d.ts files here inside this repository or
  • add them outside e.g. at DefinitelyTyped.
Related issue which might be solved by the above

Besides the actual types I had another problem: even with the usual no-types-available approach (adding a separate .d.ts file with declare module "@meilisearch/instant-meilisearch";), I could not immediately use the lib with TypeScript and Webpack. Typescript claimed the Module is no function. I worked around it by modifying the source directly and exporting the function (instead of default export).

How to exclude 0 count facets? How to make facets stay in the list?

I have two simple questions. I created a earch prototype with algolia and I'm now trying the same code to work with meilisearch.

First issue:
I have have a facet called author. Every document has exactly one author. If I click on one facet, on algolia all the autors with their count remains.

Adam (10)
Abraham (5)
Rossier (1)

If I select Adam on meilisearch the result is:
Adam (10) (active)
Abraham (0)
Rossier (0)

How can I exclude facets with count 0? They make my result quite big.

Second issue:
And how can I realize the same behaviour like on algolia to have non-disjunktive facets?

Adam (10) (active)
Abraham (5) (remain selectable)
Rossier (1) (remain selectable)

Thanks for any hints.

Use match instead <em> tags

Currently, we use <em> tags and we replace them by the specified tag, but it would be better to use the match options in query parameters (during the search) instead.

Pagination issues

Noticed pagination stops working (with react) once you go past the number of items fetched initially and there's no future offset sent to grab more pages. Not sure if this is by design, but it feels more like an oversight.

I've modified my local copy like so:

transformToMeiliSearchParams: function (params) {
      const searchInput = {
        q: params.query,
        facetsDistribution: params.facets.length ? params.facets : undefined,
        facetFilters: params.facetFilters,
        attributesToHighlight: this.attributesToHighlight,
        limit: this.limitPerRequest,
        offset: params.page * this.limitPerRequest
      }
      return removeUndefinedFromObject(searchInput)
    },

And removed the page check/splice in parseHits. That splice functionality could probably stay if you detected how many were grabbed at a time and the per page limit and did some calculations on that.

Improve tests

State of tests

As of now we only tests a few instant-meilisearch return in tests/base.test.js. Those are not enough to ensure that every option of instant-meilisearch works properly.

Solution

More integration tests should be added. In a perfect world, every parameter should be tests with different values. But we can start with an mvp of tests.

Example

  • with/without pagination widget
  • with/without facets widget
  • ...

Add attributesToRecieve as an param

Hey guys, first of all thanks for the awesome work you provided!

I have a little question: As described in your Documentation there is an option to specify which attributes to receive from a query. I tried to configure it, but sadly it isn't considered in the transformToMeiliSearchParams-Function

https://github.com/meilisearch/instant-meilisearch/blob/master/src/index.js#L16-L22

Was there a specific reason to not include this? If not, is there a possibility to add this in a future release?

Thanks!

yarn lint is not running

In preparation for #121 I tried running through the contributing guidelines, however, the step yarn lint was not running successfully on a fresh checkout. The commands given before that were fine.

yarn lint
yarn run v1.22.10
$ eslint --ext .js,.ts,.tsx .

Oops! Something went wrong! :(

ESLint: 7.12.1

ESLint couldn't find the config "react-app" to extend from. Please check that the name of the config is correct.

The config "react-app" was referenced from the config file in "/Users/bb/src/meilisearch/instant-meilisearch/playgrounds/react/package.json".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

After yarn add -D eslint-config-react-app, it still failed because of eslint-plugin-flowtype (and a few others) it runs... but it does not terminate, at least not within a few minutes:

$ yarn lint
yarn run v1.22.10
$ eslint --ext .js,.ts,.tsx .
Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting.

It just keeps one core busy at 99% CPU.

I'm using node 14.14.0 and these are my changes compared to current master:

--- a/package.json
+++ b/package.json
@@ -39,16 +39,23 @@
     "@babel/preset-env": "^7.10.4",
     "@rollup/plugin-commonjs": "^15.0.0",
     "@rollup/plugin-node-resolve": "^10.0.0",
+    "babel-eslint": "^10.1.0",
     "babel-jest": "^26.1.0",
     "eslint": "^7.5.0",
     "eslint-config-prettier": "^6.11.0",
+    "eslint-config-react-app": "^6.0.0",
     "eslint-config-standard": "^15.0.1",
+    "eslint-plugin-flowtype": "^5.2.0",
     "eslint-plugin-import": "^2.22.0",
     "eslint-plugin-jest": "^24.0.0",
+    "eslint-plugin-jsx-a11y": "^6.4.1",
     "eslint-plugin-node": "^11.1.0",
     "eslint-plugin-prettier": "^3.1.4",
     "eslint-plugin-promise": "^4.2.1",
+    "eslint-plugin-react": "^7.21.5",
+    "eslint-plugin-react-hooks": "^4.2.0",
     "eslint-plugin-standard": "^4.0.1",
+    "eslint-plugin-vue": "^7.1.0",
     "jest": "^26.1.0",
     "jest-watch-typeahead": "^0.6.0",
     "prettier": "2.1.1",

I also upgraded all outdated packages but it did not help. I created a PR to share the current WIP.

Should we remove the ".lock" file?

We are thinking about removing (or not) the .lock file of this package.

Since this discussion concerns all the JS tools of the MeiliSearch organization, we created a centralized issue: meilisearch/integration-guides#53.

If you are interested in this topic, please read it and feel free to share your (professional or not) experience with us on this main issue. Every opinion would be really helpful! 😁

question: incompatible meilisearch versions

First of all, thanks for the great work on meilisearch. It's a really awesome product.

I have a question or a remark regarding upgrading. Today, I upgrade from: @meilisearch/instant-meilisearch: 0.2.2 → 0.2.4. On the server-side, we were using meilisearch: 0.16.1 and the meilisearch instance was running meilisearch-http: 0.14. This broke the setup for us. Everything was still working but the queries no longer returned any hits. Once I upgrade to meilisearch: 0.16 everything worked fine again once I had deleted and re-created the index.

question: can we make a note somewhere so that if someone else experiences this issue that we have it documented? Or is it always the case that old client versions cannot query newer server versions? Because vice versa worked fine for us. For example, we had upgraded to meilisearch: 0.16.1 on our nodejs backend and it worked fine with meilisearch-http: 0.14.

Thanks for the feedback. Would love to help if I can improve some documentation!

Converting the project to typescript

Proposition

As this is a small project, converting it to typescript is a task that should not be that hard.

Benefits

If the project is in typescript, we can generate a typed.d.ts file containing all the types from this project. This file, in turn, would make this library available for TypeScript user.

More types mean fewer errors, an easier coding experience, and overall better readability of the code.

Cons

Adding typescript brings some complexity in configuration and requires more knowledge to maintain than a vanilla JS project.

Related to #118

React Native compatibility hint

Hi!

I've just experimented with replacing Algolia with Meilisearch in my react-native application. I had a bit of a problem for a while which turned out to be that I needed a polyfill for URL on RN. I added https://www.npmjs.com/package/react-native-url-polyfill to my project which resolved the issue.

Leaving here more for the benefit of future react native developers than anything, however, it might be a good win for some documentation since that seems to be all that's needed to get Meilisearch in more react native apps as far as I can tell :)

Remove warnings in React code-sandbox

Currently, we get these warnings in the console in React codesandbox:

Warning: Failed prop type: The prop `hits[0].objectID` is marked as required in `Hits`, but its value is `undefined`.
Warning: Each child in a list should have a unique "key" prop.

Check the render method of `Hits`. See https://fb.me/react-warning-keys for more information.
    in li (created by Hits)
    in Hits (created by AlgoliaHits(Hits))
    in AlgoliaHits(Hits) (created by Context.Consumer)
    in ConnectorWrapper (at App.js:55)
    in div (at App.js:53)
    in InstantSearch (at App.js:37)
    in div (at App.js:25)
    in App (at src/index.js:8)
    in StrictMode (at src/index.js:7)

Add more documentations

This tool is not obvious to use and our users need more documentation about how to use Instant Search + instant-meilisearch. The sandbox is not enough.

Two steps:

  1. Find a place for this kind of "tutorials": a examples folder? A longer README?
  2. Write them

Here is a non-exhaustive list of the step 2:

⚠️ the meilisearch-react and meilisearch-vue demo might need the same docs! But let's start with this repo first.

Problem with replaceAll

Hey all

I know this is early in the modules life but I stumbled on this and it's exactly what I was about to build out. Gave it a go and it wasn't rendering the hits so I did some debugging and it looks like these lines https://github.com/meilisearch/instant-meilisearch/blob/master/src/index.js#L36-L37 are silently failing.

As a stop gap I had to use https://github.com/es-shims/String.prototype.replaceAll

Looks like your demo app has the same problem so hope this helps :)

Edit: This was with react fyi

Named export vs Default export

Named or Default

As of today, instant-meilisearch uses default export. This means that in order for a user to use instant-meilisearch in an ES environment he has to import it this way:

import instantMeiliSearch from 'instant-meilisearch'

The suggestion this issue considers is changing from this default export to a named export:

import { instantMeiliSearch } from 'instant-meilisearch'

Should we change our default export to a named export?
Here are a few arguments, if you have any other feel free to share it here/

Pro's of default search

  • More common, or at least they used to be more common. There is no exact data on it. Although, I observe a change in this tendency.
  • Can be considered Easier to use, as only one export where you can change the name offers no complication to use it. import whatIwant from 'instant-meilisearch' instead of import { instantMeiliSearch } from 'instant-meilisearch'.
    Which can also be done with named exports import { instantMeiliSearch as whatIWant } from 'instant-meilisearch', which if you use VS code should be auto-completed and thus present no difficulties to do.
  • Changing it now would be breaking change (but the earlier, the better?) @bb
  • It seems that Typescript suggest using default exports in the red flag category of their documentation. But this is not really clear and based on this stackOverFlow post where a user that prefers named exports mentions the redflag.
  • No changes needed in the code as it is already a default export
  • vue-instantsearch and algoliaSearch uses default export but not react-instantsearch
    import InstantSearch from 'vue-instantsearch';
    import algoliasearch from 'algoliasearch/lite';
    import { InstantSearch, SearchBox, Hits } from 'react-instantsearch-dom';
    

Con's of default search

The first 5 arguments comes from this typescript book that has 12k stars on github we learn that: default is bad. I would suggest reading the article as it showcases it better.

  1. Poor discoverability: intellisense works best with named exports. For example this issue
  2. Because of 1., lack of autocompletion
  3. CommonJS interop: Node users have an horrible experience because of the way default exports are transpiled into cjs: const {default} = require('module/foo');. This can be avoided thanks to bundlers ( like rollup ). So this is not a problem for us. meilisearch/meilisearch-js#661
  4. Avoiding typos: Only one way to import, avoiding having it imported differently in multiple files of a same project import Foo from 'foo', import foo from 'foo'. Instead you have to import it with the right syntax import { Foo } from 'foo'
  5. Bad compatibility with dynamic imports: Dynamic imports is when you import modules dynamically during execution instead of at the start using await import(..).
    Default import:
    const HighCharts = await import('https://code.highcharts.com/js/es-modules/masters/highcharts.src.js');
    HighCharts.default.chart('container', { ... }); // Notice `.default`
    Instead of named import:
    const {HighCharts} = await import('https://code.highcharts.com/js/es-modules/masters/highcharts.src.js');
    HighCharts.chart('container', { ... }); // Notice `.default`
  6. Linking stackOverFlow post from the pros category because the user is against default exports.
  7. In this article: 4 Best Practices to Write Quality JavaScript Modules from February 26, 2020: 1.Prefer named exports
  8. Complication when creating types file in typescript #118

Vote

What do you prefer:

  • Named exports 👍
  • Default exports 🎉

Warning: Invalid prop: type check failed for prop "showMoreLimit". Expected Number got String

When using show-more-limit in ais-refinement-list whether the prop is in string or in int the same "error" is thrown in the console. I put error in quotes because it still works even with this error thrown. It work if you put a string or a int.
Official documentation of showMoreLimit

Vue warn]: Invalid prop: type check failed for prop "showMoreLimit". Expected Number with value 15, got String with value "15".

Example

<ais-refinement-list attribute="genre" show-more show-more-limit=15 />

Communicate MeiliSearch error to instantSearch

In case of error, MeiliSearch sends it usual body to instantSearch. Resulting to this:

instantsearch.js@4:2 Uncaught (in promise) Error: Uncaught, unspecified "error" event. ([object Object])

We should either stop the return process to instant search or find how to communicate it properly to instantsearch

Make end to end tests work on CI (ubuntu)

As for now we are using angular built-in protector/jasmine/Selenium stack to tests if the playground of angular generated the correct elements with instant-meilisearch.

Unfortunately, these tests are not possible on ubuntu-latest as ChromeDriver is not installed.
We should find a solution to make the CI compatible with this tests as it ensures the most the working state of instant-meilisearch

[16:45:56] I/launcher - Running 1 instances of WebDriver
[16:45:56] I/direct - Using ChromeDriver directly...
[16:46:01] E/runner - Unable to start a WebDriver session.
[16:46:01] E/launcher - Error: WebDriverError: unknown error: Chrome failed to start: exited abnormally.
  (unknown error: DevToolsActivePort file doesn't exist)
  (The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
  (Driver info: chromedriver=88.0.4324.96 (68dba2d8a0b149a1d3afac56fa74648032bcf46b-refs/branch-heads/4324@{#1784}),platform=Linux 5.4.0-1039-azure x86_64)
    at Object.checkLegacyResponse (/home/runner/work/instant-meilisearch/instant-meilisearch/playgrounds/angular/node_modules/selenium-webdriver/lib/error.js:546:15)
    at parseHttpResponse (/home/runner/work/instant-meilisearch/instant-meilisearch/playgrounds/angular/node_modules/selenium-webdriver/lib/http.js:509:13)
    at /home/runner/work/instant-meilisearch/instant-meilisearch/playgrounds/angular/node_modules/selenium-webdriver/lib/http.js:441:30
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
[16:46:01] E/launcher - Process exited with error code 100

see https://github.com/meilisearch/instant-meilisearch/pull/293/checks?check_run_id=1963073082

Improve documentation

The documentation isn't really clear if we want to call some specific routes and not only get results.
Combined with this issue, it makes it really hard to understand how it works and what we need to do in some specific use cases.

This could be an example to add to the documentation :

import { instantMeiliSearch } from "@meilisearch/instant-meilisearch";

const searchClient = instantMeiliSearch(
  "https://demos.meilisearch.com",
  PRIVATE_API_KEY
);

 const getIndexList = async () => {
    const indexes = await searchClient.client.listIndexes();
    console.log(indexes);
 };

Change master branch to main

Let's be allies and make this change that means a lot.

Here is a blog post that explain a little more why it's important, and how to easily do it. It will be a bit more complicated with automation, but we still should do it!

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.