corysimmons / blog Goto Github PK
View Code? Open in Web Editor NEWUsing Github Issues as a blogging platform.
Using Github Issues as a blogging platform.
Is it not a strange fate that we should suffer so much fear and doubt for so small a thing?
Typescript has been around since 2012, but it wasn't until a few years ago that it really started picking up speed. A lot of famous JavaScript library developers started switching to it (particularly in the Angular, and then React, communities—and now everywhere). This was fine to ignore at the time because it didn't affect me. But in the past few months I keep noticing this trend in job descriptions: "extensive Typescript experience".
I'm not new to types. I wrote a pretty large open-source project using Flow (Facebook's challenger to Typescript), and I remember thinking Typescript was better for a few reasons:
any
type to everything, whereas you can just "forget" to type certain files in a project with Flow. So Flow's biggest selling point is actually a weakness.In programming, there are two types of languages: loosely typed and strongly typed.
JavaScript is loosely typed. If I say var x = 1
then I (or some library) can accidentally re-assign that x
var doing something like var x = "1"
later on.
So think about that for a second. You are expecting x
to be a number
of 1
but it got changed to a string
. This can frequently introduce bugs. Like... you are lying, or new, if you haven't ran into a bug like this hundreds of times in the past.
Just for type safety, I wouldn't say introducing Typescript is worth the overhead. I don't run into type bugs very often so it's not a huge selling point for me. The real benefit of using Typescript is all the annotations your code editor gives you now. Holy crap your code editor will light up like a Christmas Tree.
So I create an type interface (which is just something that looks like a JS object) and then feed that into a React component, and 💥—if my component doesn't have the right props passed into it, then VSCode let's me know.
Now I never have to worry about if I forgot a prop that my component needs.
These type definitions also act as pretty damn good layer of automated documentation.
I'm having a hard time holding option
and hovering that IPost
while simultaneously doing the keyboard shortcut and dragging to get a screenshot, BUT IF I COULD, it would show you a popup with:
export interface IPost {
title: string
body: string
author: string
}
Think about how handy that is. 90% of the documentations we use are API References, and this is effectively that.
Think about that co-worker who writes all kinds of spaghetti and never documents it. Now you'll know the required (and optional) parameters, and types, of every function they push to the project. 🔥
There's still a place for a greeting page with a getting started and some demos, but for the most part, yes, people should stop writing docs. Traditional docs are frequently outdated/broken and frequently a huge source of frustration for me.
Typescript can't lie or forget to update docs, so, combined with decently-named functions and parameters, it's strictly better.
It is and isn't. It's intimidating at first, so I highly suggest you make a ☕️ then speed-watch a video course on it to at least have a compass.
Then it takes a small project to get used to, but after that you'll be competent enough to fall in love with it like so many have before you.
You really don't have a choice. Like I said, Typescript is more-and-more frequently appearing in job descriptions, and being used in every significant project on GitHub. Unless you're the CEO/CTO, it's only a matter of time before you're asked to learn it. Might as well start now. You'll like it. 😇
Stay safe. ❤️
I'm either dumb (likely) or our current practices with testing and the entire ecosystem is pretty broken. I've always wanted apps I work on to be really well tested, but it feels way harder than it should be.
First, why should we write tests? What's the point? What do we get out of it?
There is a two-part answer that addresses all of those questions:
That's it. We want to do our best to make sure our users aren't fighting with our app, and we don't want to have to check every single little thing every time someone opens a Pull Request.
With those two goals in mind, let's proceed with some of my gripes before getting to a solution, then get back to more griping.
Let's get this over with because it's one of my biggest pet peeves in the testing community.
Mocks are one of the stupidest concepts I've ever tried to wrap my head around—and I have tried for almost a decade, asking many professionals, because I thought I was simply missing something, but it turns out I was right and mocking really is just dumb for the most part.
The idea behind mocks goes something like...
Let's test something, but instead of using the function or data that the real app would use, let's just completely make something up that way it is faster and easier to set up.
How does this actually confirm whether a user is experiencing your app the way it was intended?
Think about it, you want to test whether a user can create an account on your website, so you begin writing a test for it and realize you have to hit your API to actually create the account in the database.
Well, you don't want to actually create the account in the database, so instead, you just pretend the API did what is was supposed to do, and everything works as expected.
Sweet. Your tests pass and everyone is happy until thousands of users can't make an account. How would you know your API had changed? How would you know the frontend needed to change to reflect the API changes? You never actually talked to your API. Your tests always just played around with hard-coded JSON in a make-believe fantasy world. 👇
Are mocks easy to setup and use? Yes. Are you purposefully tricking yourself and every company stakeholder into thinking your app is well-tested? Yes. This isn't a good trade-off.
So what's the alternative? How do you actually test forms and APIs and stuff without having to actually inject junk into the database?
There's no way around this. If you actually want to test your app in a confident manner without dangerously cluttering your production db with fake data from your tests, you have to set up a test environment.
This takes a bit of setup, but once you nail the pattern, you can create a custom test env for any app. It mostly revolves around replicating your database and creating pretty thorough seed files with plenty of real-ish data.
You want to work with as much real-ish data as possible. You can create a script to:
You can/should use this script for your staging env as well to keep it as up-to-date with real-ish data as possible too.
Warning Please be sure to have backups upon backups of your production db in case you screw something up and need to rollback. This shouldn't need to be said. If you find it difficult to setup a CRON job to get a production db dump and send it to S3, then at least make sure your db host are backing up your db (you will probably have to pay a bit for this service).
Now you can confidently create a test db, do whatever you want to it, then delete it. You can do this billions of times, locally or in CI, with or without considering a team, and do whatever you want to that db.
Almost never. Stop going back to your evil mock ways.
"What if I have to communicate with a 3rd party API?"
This is going to hurt you because it's extra work, but just create a test account for that 3rd party API, add that API token to your .test-env
file. And then create another script to export production data from the original account, fake PII, and import it into the test account.
Now that I think about it, we should only be exporting data (from a production db or a 3rd party API) via whitelisting records. PII can always be introduced to an API.
This might seem like overkill, and if you're only using the API for a couple payloads, then mocking is probably fine, but if your app heavily relies on specific 3rd party APIs, then the freedom to test against their actual endpoints, libraries, and SDKs will save you time, and quickly catch a lot of bugs, in the long run.
As an example, imagine your app sells stuff or takes donations and relies heavily on Stripe. You haven't upgraded the Stripe SDK dependency in your app in a while. Let's say you're on something like 2.0.3
(these are imaginary semver versions I'm picking); several months go by and your tests have been passing; someone hacks Stripe; Stripe fixes it quickly and releases the fix as 4.3.4
.
How do you confidently upgrade to 4.3.4
? You have 400 tests that were mocked with payloads you got from 2.0.3
.
Do you go through every single test and compare its request and response to the 4.3.4
version, and tweak the mocks to match 4.3.4
? You will almost certainly make a mistake that won't be caught until something happens enough times in production that a user finally speaks up. You've potentially done millions of dollars of damage to your company.
On the other hand, if you had a test env setup from the beginning, everyone on the team could've been upgrading willy-nilly the entire time. You're not messing with real money (Stripe also appreciates the concept of test environments so you just change your Stripe API secret key in your .test-env
file et voila) so who cares if something breaks in your chore/upgrade-stripe
branch?
Fine. These are the only three instances I can see mocking being useful.
Let's stick a pin in the whining for a second to go over some testing approaches and why they fall short.
Why mount a component at all? Why do this? Does knowing a component has x
prop confirm the end-user is seeing a specific view the way it was meant to be rendered?
Pseudo example:
// some-page.tsx
const columns = ['Employee', 'Salary']
const cellData = [ /* ... */ ]
<Table
columns={columns}
cellData={cellData}
/>
// some-page.test.tsx
test('Column header should be Employee', () => {
const columns = ['Employee', 'Salary']
const mounted = mount(<Table columns={columns} />)
expect(mounted).toBeVisible()
expect(mounted.props.columns).toEqual(columns)
expect(mounted.someSelectorFunc('th:first-of-type')).toHaveText('Employee')
// ✅ Passes!
})
Sweet, it passed. Does this test, or any component test really, 100% confirm a user is able to see that Employee
?
Employee
." And holy crap once you start actually clicking on things and waiting for elements to hide, pages to change, elements to transition into view, etc. it becomes a huge pain. Like, look at all the stuff you have to do to click on a button and see if it triggered some element to be modified: https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warningSo combos like Enzyme or Testing Library + Jest are pretty useless.
Kent C. Dodds seems very smart when it comes to testing, and probably the most famous person in the "testing JS" sphere (in fact he has a course called Testing Javascript), but even his testing library seems like it only provides a simpler way to mount a component then adds a couple shortcut functions that just abstract CSS selectors a smidge (shout-out to trying to make devs more a11y-friendly by promoting selectors like "findByText" and "getByRole").
Don't get me wrong Testing Library is 1000x easier/better than Enzyme, but it still is just looking at some JSDom, and working with that JSDom is incredibly frustrating.
All of this said, Jest is actually pretty amazing. It is super lightweight, fast, has a really nice watch mode, has a nice colorized CLI, all kinds of configuration options, etc.
Jest is just a great tool. Good job Jest.
At first glance, E2E seems like a good idea:
Tools like Selenium, Nightwatch, TestCafe, Cypress, Playwright, etc. have been around for a very, very, long time. People have been using them to create all kinds of tests for a decade or so.
A few problems with these...
If you run these headed (i.e. you can see the browser pop up and click around) then they are slllloooowwww. It just takes the browser forever to start.
We still have to write code to click things, interact with forms, etc. but at least we don't have super weird/awkward stuff like act to fight with.
Honestly, Cypress handles state change like I would expect.
Look at this https://www.cypress.io/blog/2019/02/05/modern-frontend-testing-with-cypress#toggle-completed-state
That's exactly what I want. To be able to write a test like this:
test('When the Create Account button is clicked, it gets removed from the page, and a loading spinner gets added', () => {
const btn = find('#create-account-btn')
btn.click()
expect(btn).not.toBeInTheDocument() // even if it animates out
expect(find('#spinner')).toBeInTheDocument() // even if it animates in
})
Cypress even waits on animations and such.
I cannot emphasize how much I hate that act()
warning so Cypress immediately beats everything.
Imagine trying to learn exactly when/how/where to use act()
and all the gotchas, and then teaching your team that. It won't happen. It isn't worth it. Just use Cypress.
Cypress is slow. Run Cypress in headless mode via cypress run
(instead of cypress open
).
Cypress is also a real pain to get working in Docker (especially on M1 Chips). I half suspect it is on purpose. If their official Docker images worked, why would anyone purchase their Cypress Dashboard product? I wouldn't. So there is a lot of lock-in with Cypress.
Playwright looked like the best of all worlds:
Playwright doesn't work with Yarn 2's PnP. Its codebase is riddled with references to node_modules
(which don't exist in Yarn 2 PnP) and like most OSS maintainers, no one is excited to upgrade their project to support a specific tool.
Playwright can't into Typescript paths: microsoft/playwright#7121
I keep coming back to this idea that the answer to all my testing problems revolves around screenshots somehow. My reasoning is what if I forgot to test for a sidebar visibility in an E2E test suite? Would the E2E runner care? No. But with screenshots, yes it would. It would also test for the visibility of certain elements without me having to actually type that stuff out.
For instance, in any other test runner, I'd have to do something like:
test('Clicking the submit button shows a success message and removes the submit button then adds a new newsletter signup form', async () => {
find('#btn').click()
expect(find('#success_message')).isVisible()
expect(await find('#btn')).not.toBeInTheDocument()
expect(await find('#newsletter_form')).toBeInDocument()
expect(await find('#newsletter_form_checkbox')).isVisible()
expect(await find('#newsletter_form_checkbox').attr('checked')).isFalse()
expect(await find('#newsletter_form_btn')).isVisible()
expect(await find('#newsletter_form_btn').attr('disabled')).isFalse()
})
What if I could combine everything after the "Do some interaction" part, every assertion (and if you're writing really thorough tests, you need to write a lot), into a single assertion like so:
test('Clicking the submit button shows a success message and removes the submit button then adds a new newsletter signup form', (oldSnapshot) => {
find('#btn').click()
wait('500ms')
const newScreenshot = screenshot()
expect(oldSnapshot).isSameImage(newScreenshot)
})
👆 So much easier. Any dev can pick this up immediately. Multiple that by hundreds of tests in a project.
There are projects like:
Percy and Happo are expensive and lock-in. Chromatic is expensive and lock-in, and only works with Storybook (sidenote: I'm not a big fan of Storybook or Docz either, you can probably generate cleaner/more-helpful docs pretty easily from types, mocked 😱 data, and some loop fn).
jest-screenshot sounds very promising but it's unmaintained and I ran into another Yarn 2 problem Prior99/jest-screenshot#83
If jest-screenshot is to be believed then jest-image-snapshot is slow. I also had some flaky tests with jest-image-snapshot.
Almost everything in the 2021 JS ecosystem feels off (with the exception of a few tools like NextJS which is so nice).
I don't think my ideal testing tool exists right now.
defaultMs = 1000
like Cypress, Playwright, and https://testing-library.com/docs/dom-testing-library/api-async#waitfor do.--watchAll
, incremental, etc).node_modules
and has to be Yarn 2+ friendly.I think it might be pretty straightforward to develop by forking jest-screenshot, or just creating something from scratch using some of the libraries jest-screenshot uses.
There are new versions of Javascript (ES6, ES7) full of new features and a ton of syntax shortcuts. For example:
() =>
2
...is shorthand for an anonymous function that returns 2.
As with everything in programming, people can't agree on anything and browsers are slow to implement stuff. We'd have jetpacks before browsers implemented it correctly/consistently so some cool people made some things called "transpilers" that convert ES6+ to usable old-fashioned Javascript (ES5). The most popular one is Babel.
Here's what it converts the above code to.
Babel works with just about any build process, and is extendable with modular plugins.
The easiest/cleanest way is installing some npm packages and configuring it in package.json
. We'll make an npm script for it so we can run something like npm run dev
from the terminal.
cd ~/playground/babel
echo '{}' > package.json
(create a valid JSON file so we can npm install
to it)npm i -D babel-cli babel-preset-es2015 babel-preset-stage-0
(this will install Babel's CLI, ES6 (aka: ES2015), and ES7 support)"babel"
object to package.json
. Inside it we'll tell it to use the presets we installed:"babel": {
"presets": [
"es2015",
"stage-0"
]
}
test.js
file and put it in a src
folder. src
(sometimes lib
) is your original code. We transpile stuff from src
to the sibling dist
folder.Protip: You transpile CSS (Sass, Stylus, LESS, PostCSS, etc.) and HTML (Pug/Jade, Haml, etc.) as well. Currently most companies organize this stuff as
src/css
,src/js
,src/html
, etc. but I think component architecture is a better solution for larger projects so try it out if you can.
node_modules/.bin/babel src -d dist
to ensure ES6/7 files in src
are being transpiled to vanilla Javascript in the dist
folder.node_modules/.bin/babel -h
you'll see it has a watch flag.node_modules/.bin/babel -w src -d dist
and change some stuff in src/test.js
to make sure it's being transpiled on saves."scripts"
object in package.json
as "dev"
then just run npm run dev
(package.json
scripts don't need the node_modules/.bin/
part).src
and everything will work fine.package.json
should look like this (if you copy this make sure you run npm i
before using it):
{
"devDependencies": {
"babel-cli": "^6.10.1",
"babel-preset-es2015": "^6.9.0",
"babel-preset-stage-0": "^6.5.0"
},
"babel": {
"presets": [
"es2015",
"stage-0"
]
},
"scripts": {
"dev": "babel -w src -d dist"
}
}
Now you're able to use ES6/7. Not only that, but you're using package.json
scripts to perform tasks which is pretty nice as long as a CLI for a particular tool is available (if one isn't, consider doing everyone a favor and making one).
dist/css/maps
folder.src/css
before it watches.Linting is cool. It keeps people from making stupid mistakes and can make your code a lot neater/easier to read.
Not linting is how you end up with spaghetti code that no one can read.
Lint everything. HTML, CSS, JS, Python, Ruby, PHP, etc.
ESLint lints JS really nicely.
There was JSHint and JSLint, but they were hard to extend so someone made a JS linter that was easy to extend called ESLint. It's now the standard.
Agree with your team on a coding style. This part isn't super important and there are a ton of well thought out configs out there already like AirBnB. What's important is you all agree to do the same code style!
Use npm. It's easy and portable.
cd ~/playgrounds/eslint
echo '{}' > package.json
npm install --save-dev eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y eslint
(AirBnB usage)Note: I feel ya. All of these npm packages seem silly, but it's the whole Unix philosophy of making everything modular. It's a bit annoying, but overall it's usually a good thing.
eslint --init
to make an ESLint config file. In that file, you could add something like "extends": "airbnb"
, but that's newb stuff. Do we really need yet another config file?package.json
add an "eslintConfig"
object with "extends": "airbnb"
in it.Protip: If you're configuring some npm package, see if you can put it in
package.json
instead of having 20 config files polluting the root of your project.
node_modules/.bin/eslint
in terminal (and this is actually cool/useful for the --fix
flag), but you don't want to be switching between terminal and editor after every keystroke to see if you made a mistake, so install an editor plugin for ESLint. Here's Atom's. ESLint is insanely popular so there is almost certainly a plugin for your editor.alert()
is throwing an error!Config it!
"eslintConfig": {
"extends": "airbnb",
"env": {
"jquery": true
},
"rules": {
"no-alert": "off"
}
}
Now you're linting your JS like a pro. Enjoy your impending OCD.
Type-checking is pretty sweet and helps prevent lots of hard-to-find bugs. If you're using default named params for all your functions, it's fairly easy to integrate some simple type-checking.
A real-world example might make this more clear!
Frankenstein (the doctor -- not the monster) has some cats. Some of them have less legs than they are supposed to have. He can add more legs, but he developed some compulsive disorder and can only add legs if he's allowed to double them. As @mpjme would say, this is the kind of problem you encounter every day in Enterprise-level Coding™.
const typeCheck = (interface, argsObj) => {
for (const param in interface) {
if (typeof argsObj[param] !== interface[param]) // Comparing the arguments to the type interface.
throw new TypeError(`${argsObj[param]} should be a ${interface[param]} type!`)
}
}
// Type interface
const kittenTyping = {
nickname: 'string',
legs: 'number'
}
const frankenKitten = ({nickname = 'Hops', legs = 3}) => {
typeCheck(kittenTyping, {nickname, legs}) // interface, argsObj
console.log(`*sewing sounds* 🙀 Now ${nickname} has ${legs * 2} legs! 😻`)
}
// Lets saw a leg off so Hops ends up with a normal amount of legs (4), then let's rename him Skip.
frankenKitten({nickname: 'Skip', legs: '2'}) // TypeError: 2 should be a number type!
frankenKitten({legs: 2}) // Ah, that's better!
Now we're catching type bugs that otherwise would've leaked through since clever JavaScript will "intelligently" (aka: magically) convert that '2'
string into a number during the multiplication step.
Protip: Place
typeCheck()
right abovereturn
statements to help prevent bugs. If you encounter a bug, just addtypeCheck()
s higher and higher in your code until you find the culprit (then clean up after yourself except for that one right above yourreturn
). Psst, the culprit usually looks likeObject.<anonymous>
in the terminal stack trace.
This isn't great because it requires us to pass all the interface params to argsObj
whether we're using them or not.
// Hops is still a good name!
frankenKitten({legs: 2})
// ...
typeCheck(kittenTyping, {legs}) // Error! We always need to pass whatever params match in the function defaults and the interface.
typeCheck(kittenTyping, {nickname, legs}) // Works, but smells a bit.
I'm sure there's some clever way to capture deconstructed variables without having to explicitly pass all of them within typeCheck()
, but tbh, this solution isn't as nice as existing tools and I'm getting hungry.
It is, however, a pretty good way to start sneaking basic type-checking into stuff without introducing any dependencies.
It also educates and promotes the discussion of integrating one of those more feature-rich solutions. Coworkers should begin to see the value in type-checking, but probably feel kind of gross about all the typeCheck()
functions littering your code, or they'll want to typecheck arrays and such...
That's the purrfect time to sell something like Flow/TypeScript/Elm. 😽
I very randomly got curious how to compare objects in Javascript so opened DevTools and began tinkering. I remembered you could use JSON.stringify()
to do this, and it even works with deep equality. Example: JSON.stringify({a: 1, b: {c: 2}}) === JSON.stringify({a: 1, b: {c: 2}})
But it breaks the second those properties aren't in the same order. For instance, JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1})
fails.
Like most deep things, I figured a recursive function would be necessary, and out of laziness, started Googling around to see if there was a really slick ES6 way to do it (like some fresh/obscure Object.deepEqual
native method I hadn't learned yet or something), but alas, there wasn't.
I was certain Lodash had something. And of course it did. And in practice, I would probably use it because it's probably better tested for fringe cases, probably pretty performant, and Lodash is familiar to all developers and comes with lots of usage documentation.
But I was curious how it worked so I dove into Lodash code for a second and it was depending on sooo many little stupid building block modules for every single thing, that reverse engineering it made the problem "not fun".
So, I just figured I'd try to solve it myself (which I'm proud to say I did really quickly—usually these things hamstring me for a couple hours but I solved this one instantly) and submit it to StackOverflow (since its an incredibly popular question, and my solution is elegant, maybe I could get some of that sweet SO karma), but all the threads for it were locked, so HERE WE ARE. 😩
const nestedObj1 = { b: 2, c: { e: 3, d: 4 }, a: 1 }
const nestedObj2 = { a: 1, b: 2, c: { d: 4, e: 3 } }
const nestedObj3 = { a: 1, b: 2, c: { d: 4, e: 9999999999999 } }
const shallowObj1 = { a: 1, b: 2 }
const shallowObj2 = { b: 2, a: 1 }
const shallowObj3 = { a: 1, b: 9999999999999 }
// Recursively sorts the keys and returns a fresh object
function sortObj(obj) {
return Object
.keys(obj)
.sort()
.reduce((acc, curr) => {
if (typeof obj[curr] === 'object') {
acc[curr] = sortObj(obj[curr])
} else {
acc[curr] = obj[curr]
}
return acc
}, {})
}
// Does that stringify comparison stuff to the _now 100% recursively sorted_ objects
function deepEqual(obj1, obj2) {
return JSON.stringify(sortObj(obj1)) === JSON.stringify(sortObj(obj2))
}
deepEqual(nestedObj1, nestedObj2) // true
deepEqual(nestedObj1, nestedObj3) // false
deepEqual(shallowObj1, shallowObj2) // true
deepEqual(shallowObj1, shallowObj3) // false
That's it. Curious how it performs vs Lodash (maybe my next post should be figuring out how to do isolated function benchmarks 🤔), but it seems to work well in these few generic tests.
Update: As it turns out my implementation is about 35% slower than Lodash's. 🤷♂️ Ah well, it was fun to tinker with.
I'm starting to wonder if we should just use named params for everything. They provide a lot of flexibility with no side-effects that I know of.
Here's how you do it:
const foo = ({a = 1, b = 2}) => console.log(`a: ${a}\nb: ${b}`)
foo({})
// a: 1
// b: 2
foo({b: 3})
// a: 1
// b: 3
foo(undefined, 3)
.Equal sign when setting defaults. Colon to assign when calling.
Yes, now you have to at least pass an empty object to your functions, and yes, we're forcing people to pass objects at all times, but there's probably an argument to be made about objects being more flexible anyway, and all-in-all, these are small prices to pay for how nice default arguments and named params are.
Named params have always been one of my favorite features of a language. JS doesn't seem to support a super-clean (still have to pass these as objects) way to do this, but at least now we can do it somewhat elegantly.
Now... if only there was a way to type-check those params...
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.