├── public ├── _redirects ├── favicon.ico ├── manifest.json └── index.html ├── .eslintignore ├── .npmrc ├── .prettierignore ├── src ├── setupTest.js ├── docs │ ├── docs.mdx │ ├── docs.js │ └── mock-query-response.json ├── screens │ ├── user │ │ ├── user-context.js │ │ ├── components │ │ │ ├── repo-filter.js │ │ │ ├── __tests__ │ │ │ │ ├── repo-filter.js │ │ │ │ └── query.js │ │ │ ├── query.js │ │ │ ├── repo-list.js │ │ │ └── profile.js │ │ └── index.js │ └── home │ │ ├── docs.mdx │ │ └── index.js ├── shared │ ├── theme-provider.js │ ├── loading.js │ ├── layout.js │ └── pattern.js ├── index.js ├── global-styles.css └── github-client.js ├── .env ├── .travis.yml ├── .prettierrc ├── lint-staged.config.js ├── README.md ├── .gitignore ├── LICENSE └── package.json /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | build/ 3 | node_modules/ 4 | .docz/ 5 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | package-lock=true 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .git 2 | package-lock.json 3 | node_modules 4 | .docz 5 | build 6 | coverage 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kentcdodds/react-github-profile/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/setupTest.js: -------------------------------------------------------------------------------- 1 | import 'jest-dom/extend-expect' 2 | import 'react-testing-library/cleanup-after-each' 3 | -------------------------------------------------------------------------------- /src/docs/docs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: App 3 | route: / 4 | --- 5 | 6 | import DocsApp from './docs' 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/screens/user/user-context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const UserContext = React.createContext() 4 | 5 | export default UserContext 6 | -------------------------------------------------------------------------------- /src/screens/home/docs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | name: Home 3 | menu: Components 4 | route: /components/home 5 | --- 6 | 7 | import Home from '.' 8 | 9 | 10 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # sadly docz wants to install a different version of webpack than react-scripts 2 | SKIP_PREFLIGHT_CHECK=true 3 | REACT_APP_NETLIFY_SITE_ID=2b9c1652-1f15-4c58-89f2-290796d9fc68 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 8 4 | script: npm run validate 5 | notifications: 6 | email: false 7 | branches: 8 | only: 9 | - master 10 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": false, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": false, 9 | "jsxBracketSameLine": false 10 | } 11 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | linters: { 3 | '**/*.+(js|json|less|css|ts|tsx|md)': [ 4 | 'prettier', 5 | 'npm run test -- --coverage --findRelatedTests', 6 | 'git add', 7 | ], 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /src/shared/theme-provider.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {ThemeProvider} from 'emotion-theming' 3 | 4 | const theme = { 5 | common: { 6 | borderBottom: {borderBottom: '1px solid #eee'}, 7 | }, 8 | colors: { 9 | faded: '#666', 10 | fadedExtra: '#888', 11 | }, 12 | } 13 | 14 | export default props => 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-github-profile 2 | 3 | An app I'm going to use to teach people about React 👍 4 | 5 | ## Setup 6 | 7 | TODO 8 | 9 | ## Notes 10 | 11 | ### Folder structure 12 | 13 | The folder structure comes from [this gist](https://gist.github.com/ryanflorence/daafb1e3cb8ad740b346) 14 | 15 | I'm a big fan of it. That's why we're using it :) 16 | 17 | ## LICENSE 18 | 19 | MIT 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | .docz 24 | -------------------------------------------------------------------------------- /src/shared/loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const sizes = { 4 | small: {zoom: 0.7}, 5 | medium: {zoom: 1}, 6 | large: {zoom: 2}, 7 | } 8 | 9 | function Loading({size = 'medium', className = '', ...props}) { 10 | return ( 11 |
12 |
13 |
14 |
15 |
16 |
17 | ) 18 | } 19 | 20 | export default Loading 21 | -------------------------------------------------------------------------------- /src/screens/user/components/repo-filter.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React from 'react' 3 | import {Section, Input} from '../../../shared/pattern' 4 | 5 | export default RepoFilter 6 | 7 | function RepoFilter({filter, onUpdate}) { 8 | return ( 9 |
10 | onUpdate(value)} 16 | /> 17 |
18 | ) 19 | } 20 | 21 | RepoFilter.propTypes = { 22 | filter: PropTypes.string.isRequired, 23 | onUpdate: PropTypes.func.isRequired, 24 | } 25 | -------------------------------------------------------------------------------- /src/screens/user/components/__tests__/repo-filter.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render, fireEvent} from 'react-testing-library' 3 | import ThemeProvider from '../../../../shared/theme-provider' 4 | import RepoFilter from '../repo-filter' 5 | 6 | test('when the user changes the input, the onUpdate prop is called with the new text', () => { 7 | const onUpdate = jest.fn() 8 | const {getByLabelText} = render( 9 | 10 | 11 | , 12 | ) 13 | const value = 'a' 14 | fireEvent.change(getByLabelText(/filter/i), {target: {value}}) 15 | expect(onUpdate).toHaveBeenCalledTimes(1) 16 | expect(onUpdate).toHaveBeenCalledWith(value) 17 | }) 18 | -------------------------------------------------------------------------------- /src/docs/docs.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | import 'style-loader!css-loader!../global-styles.css' 3 | import React from 'react' 4 | import ThemeProvider from '../shared/theme-provider' 5 | import User from '../screens/user' 6 | import * as GitHubContext from '../github-client' 7 | import data from './mock-query-response.json' 8 | 9 | const fakeClient = {request: () => Promise.resolve(data)} 10 | 11 | function DocsApp() { 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 | ) 21 | } 22 | 23 | export default DocsApp 24 | export {fakeClient} 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Matt Zabriskie and Kent C. Dodds 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/shared/layout.js: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled/macro' 2 | 3 | const sizes = { 4 | phone: 768, 5 | tablet: 992, 6 | desktop: 1200, 7 | } 8 | 9 | const queries = { 10 | tiny: `@media (max-width: ${sizes.phone}px)`, 11 | phone: `@media (min-width: ${sizes.phone}px)`, 12 | tablet: `@media (min-width: ${sizes.tablet}px)`, 13 | desktop: `@media (min-width: ${sizes.desktop}px)`, 14 | } 15 | 16 | const Container = styled.div({ 17 | label: 'container', 18 | paddingRight: '15px', 19 | paddingLeft: '15px', 20 | marginRight: 'auto', 21 | marginLeft: 'auto', 22 | [queries.phone]: { 23 | maxWidth: '750px', 24 | }, 25 | [queries.tablet]: { 26 | maxWidth: '970px', 27 | }, 28 | [queries.desktop]: { 29 | maxWidth: '1170px', 30 | }, 31 | }) 32 | 33 | const Row = styled.div({ 34 | label: 'row', 35 | marginRight: '-15px', 36 | marginLeft: '-15px', 37 | [queries.phone]: { 38 | display: 'flex', 39 | justifyContent: 'center', 40 | }, 41 | }) 42 | 43 | const Column = styled.div( 44 | { 45 | label: 'column', 46 | position: 'relative', 47 | minHeight: '1px', 48 | paddingRight: '15px', 49 | paddingLeft: '15px', 50 | }, 51 | ({width}) => ({ 52 | [queries.phone]: { 53 | flex: width, 54 | }, 55 | }), 56 | ) 57 | 58 | export {queries, Container, Row, Column} 59 | -------------------------------------------------------------------------------- /src/screens/home/index.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import {navigate} from '@reach/router' 5 | import {Input, PrimaryButton, IsolatedContainer} from '../../shared/pattern' 6 | 7 | function handleSubmit(e) { 8 | e.preventDefault() 9 | const username = e.target.elements.username.value.trim() 10 | navigate(`/${username}`) 11 | } 12 | 13 | function Home() { 14 | return ( 15 | 16 |
25 | 37 | 44 | Go 45 | 46 |
47 |
48 | ) 49 | } 50 | 51 | export default Home 52 | 53 | /* 54 | eslint 55 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 56 | react/react-in-jsx-scope: "off" 57 | */ 58 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './global-styles.css' 2 | import React from 'react' 3 | import ReactDOM from 'react-dom' 4 | import {Router} from '@reach/router' 5 | import ErrorBoundary from 'react-error-boundary' 6 | import loadable from 'react-loadable' 7 | import ThemeProvider from './shared/theme-provider' 8 | import {IsolatedContainer, LoadingMessagePage} from './shared/pattern' 9 | import * as GitHubContext from './github-client' 10 | 11 | function LoadingFallback({error, pastDelay}) { 12 | if (error) { 13 | // our ErrorBoundary will catch this 14 | throw error 15 | } 16 | return Loading Application 17 | } 18 | 19 | const Home = loadable({ 20 | loader: () => import('./screens/home'), 21 | loading: LoadingFallback, 22 | }) 23 | 24 | const User = loadable({ 25 | loader: () => import('./screens/user'), 26 | loading: LoadingFallback, 27 | }) 28 | 29 | function ErrorFallback({error}) { 30 | return ( 31 | 32 |

There was an error

33 |
{JSON.stringify(error, null, 2)}
34 |
35 | ) 36 | } 37 | 38 | function App() { 39 | return ( 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 | const ui = 54 | const container = document.getElementById('root') 55 | 56 | ReactDOM.render(ui, container) 57 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-github-profile", 3 | "version": "0.2.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/core": "10.0.2", 7 | "@emotion/styled": "10.0.2", 8 | "@reach/router": "1.2.1", 9 | "date-fns": "1.29.0", 10 | "emotion": "10.0.2", 11 | "emotion-theming": "10.0.2", 12 | "graphql-request": "1.8.2", 13 | "match-sorter": "2.3.0", 14 | "netlify-auth-providers": "1.0.0-alpha5", 15 | "prop-types": "15.6.2", 16 | "react": "16.7.0-alpha.2", 17 | "react-cache": "2.0.0-alpha.1", 18 | "react-dom": "16.7.0-alpha.2", 19 | "react-error-boundary": "1.2.3", 20 | "react-loadable": "^5.5.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "npm run build:app && npm run build:docs", 25 | "build:app": "react-scripts build", 26 | "build:docs": "docz build --dest build/docz", 27 | "test": "react-scripts test", 28 | "dev": "docz dev --port 3001", 29 | "format": "prettier --write \"**/*.+(js|json|css)\"", 30 | "validate": "cross-env CI=true npm-run-all --parallel test build" 31 | }, 32 | "husky": { 33 | "hooks": { 34 | "pre-commit": "lint-staged && npm run build -s" 35 | } 36 | }, 37 | "eslintConfig": { 38 | "extends": "react-app" 39 | }, 40 | "browserslist": [ 41 | ">0.2%", 42 | "not dead", 43 | "not ie <= 11", 44 | "not op_mini all" 45 | ], 46 | "devDependencies": { 47 | "cross-env": "5.2.0", 48 | "docz": "0.12.9", 49 | "husky": "1.1.3", 50 | "jest-dom": "2.1.1", 51 | "lint-staged": "8.0.4", 52 | "lodash": "4.17.11", 53 | "normalize.css": "8.0.0", 54 | "npm-run-all": "4.1.3", 55 | "prettier": "1.14.3", 56 | "react-scripts": "2.1.1", 57 | "react-testing-library": "5.3.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/screens/user/components/query.js: -------------------------------------------------------------------------------- 1 | import {Component} from 'react' 2 | import PropTypes from 'prop-types' 3 | import isEqual from 'lodash/isEqual' 4 | import * as GitHub from '../../../github-client' 5 | 6 | class Query extends Component { 7 | static propTypes = { 8 | query: PropTypes.string.isRequired, 9 | variables: PropTypes.object, 10 | children: PropTypes.func.isRequired, 11 | normalize: PropTypes.func, 12 | } 13 | static defaultProps = { 14 | normalize: data => data, 15 | } 16 | static contextType = GitHub.Context 17 | 18 | state = {loaded: false, fetching: false, data: null, error: null} 19 | 20 | componentDidMount() { 21 | this._isMounted = true 22 | this.query() 23 | } 24 | 25 | componentDidUpdate(prevProps) { 26 | if ( 27 | !isEqual(this.props.query, prevProps.query) || 28 | !isEqual(this.props.variables, prevProps.variables) 29 | ) { 30 | this.query() 31 | } 32 | } 33 | 34 | componentWillUnmount() { 35 | this._isMounted = false 36 | } 37 | 38 | query() { 39 | this.setState({fetching: true}) 40 | const client = this.context 41 | client 42 | .request(this.props.query, this.props.variables) 43 | .then(res => 44 | this.safeSetState({ 45 | data: this.props.normalize(res), 46 | error: null, 47 | loaded: true, 48 | fetching: false, 49 | }), 50 | ) 51 | .catch(error => 52 | this.safeSetState({ 53 | error, 54 | data: null, 55 | loaded: false, 56 | fetching: false, 57 | }), 58 | ) 59 | } 60 | 61 | safeSetState(...args) { 62 | this._isMounted && this.setState(...args) 63 | } 64 | 65 | render() { 66 | return this.props.children(this.state) 67 | } 68 | } 69 | 70 | export default Query 71 | -------------------------------------------------------------------------------- /src/screens/user/components/repo-list.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import PropTypes from 'prop-types' 5 | import styled from '@emotion/styled/macro' 6 | import distanceInWordsToNow from 'date-fns/distance_in_words_to_now' 7 | import matchSorter from 'match-sorter' 8 | import {Text, Anchor} from '../../../shared/pattern' 9 | import UserContext from '../user-context' 10 | 11 | function RepoList({repos, filter}) { 12 | const matchingRepos = matchSorter(repos, filter, { 13 | keys: [ 14 | 'name', 15 | {maxRanking: matchSorter.rankings.SIMPLE_MATCH, key: 'language'}, 16 | {maxRanking: matchSorter.rankings.CONTAINS, key: 'description'}, 17 | ], 18 | }) 19 | return ( 20 |
    28 | {matchingRepos.map(repo => ( 29 | 30 | ))} 31 |
32 | ) 33 | } 34 | 35 | RepoList.propTypes = { 36 | filter: PropTypes.string.isRequired, 37 | repos: PropTypes.arrayOf( 38 | PropTypes.shape({ 39 | id: PropTypes.string.isRequired, 40 | }), 41 | ).isRequired, 42 | } 43 | 44 | const ListItem = styled.li( 45 | {padding: '25px 0'}, 46 | ({theme}) => theme.common.borderBottom, 47 | ) 48 | 49 | const Stat = styled(Text)({marginLeft: 10}).withComponent('strong') 50 | Stat.defaultProps = {tint: 'fadedExtra'} 51 | 52 | function RepoListItem({repo}) { 53 | const timeUpdated = distanceInWordsToNow(repo.pushedAt) 54 | return ( 55 | 56 |
57 | {repo.language} 58 | ☆ {repo.stargazersCount} 59 | Ⴤ {repo.forksCount} 60 |
61 |
62 | 63 | 64 | {repo.name} 65 | 66 | 67 |
68 |

69 | 70 | {repo.description} 71 | 72 |

73 | 76 |
77 | ) 78 | } 79 | 80 | RepoListItem.propTypes = { 81 | repo: PropTypes.shape({ 82 | pushedAt: PropTypes.string, 83 | language: PropTypes.string, 84 | stargazersCount: PropTypes.number, 85 | forksCount: PropTypes.number, 86 | url: PropTypes.string, 87 | name: PropTypes.string, 88 | description: PropTypes.string, 89 | }).isRequired, 90 | } 91 | 92 | function RepoListUserConsumer(props) { 93 | return ( 94 | 95 | {userData => } 96 | 97 | ) 98 | } 99 | 100 | export default RepoListUserConsumer 101 | 102 | /* 103 | eslint 104 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 105 | react/react-in-jsx-scope: "off" 106 | */ 107 | -------------------------------------------------------------------------------- /src/global-styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-box-sizing: border-box; 3 | box-sizing: border-box; 4 | } 5 | *:before, 6 | *:after { 7 | -webkit-box-sizing: border-box; 8 | box-sizing: border-box; 9 | } 10 | html { 11 | font-size: 10px; 12 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 13 | } 14 | body { 15 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 16 | font-size: 14px; 17 | line-height: 1.42857143; 18 | color: #333; 19 | background-color: #fff; 20 | margin: 0; 21 | } 22 | 23 | /* thanks to https://loading.io/css/ */ 24 | .lds-ellipsis { 25 | display: inline-block; 26 | position: relative; 27 | width: 64px; 28 | height: 64px; 29 | } 30 | .lds-ellipsis div { 31 | position: absolute; 32 | top: 27px; 33 | width: 11px; 34 | height: 11px; 35 | border-radius: 50%; 36 | background: #333; 37 | animation-timing-function: cubic-bezier(0, 1, 1, 0); 38 | } 39 | .lds-ellipsis div:nth-child(1) { 40 | left: 6px; 41 | animation: lds-ellipsis1 0.6s infinite; 42 | } 43 | .lds-ellipsis div:nth-child(2) { 44 | left: 6px; 45 | animation: lds-ellipsis2 0.6s infinite; 46 | } 47 | .lds-ellipsis div:nth-child(3) { 48 | left: 26px; 49 | animation: lds-ellipsis2 0.6s infinite; 50 | } 51 | .lds-ellipsis div:nth-child(4) { 52 | left: 45px; 53 | animation: lds-ellipsis3 0.6s infinite; 54 | } 55 | @keyframes lds-ellipsis1 { 56 | 0% { 57 | transform: scale(0); 58 | } 59 | 100% { 60 | transform: scale(1); 61 | } 62 | } 63 | @keyframes lds-ellipsis3 { 64 | 0% { 65 | transform: scale(1); 66 | } 67 | 100% { 68 | transform: scale(0); 69 | } 70 | } 71 | @keyframes lds-ellipsis2 { 72 | 0% { 73 | transform: translate(0, 0); 74 | } 75 | 100% { 76 | transform: translate(19px, 0); 77 | } 78 | } 79 | 80 | /* Thanks to https://medium.freecodecamp.org/a-step-by-step-guide-to-making-pure-css-tooltips-3d5a3e237346 */ 81 | [data-tooltip] { 82 | position: relative; 83 | display: inline-block; 84 | } 85 | 86 | [data-tooltip]::before { 87 | content: ''; 88 | position: absolute; 89 | top: -6px; 90 | left: 50%; 91 | transform: translateX(-50%); 92 | border-width: 4px 6px 0 6px; 93 | border-style: solid; 94 | border-color: rgba(0, 0, 0, 0.7) transparent transparent transparent; 95 | z-index: 99; 96 | opacity: 0; 97 | transition: 0.15s opacity; 98 | } 99 | 100 | [data-tooltip]::after { 101 | content: attr(data-tooltip); 102 | position: absolute; 103 | left: 50%; 104 | top: -6px; 105 | transform: translateX(-50%) translateY(-100%); 106 | background: rgba(0, 0, 0, 0.7); 107 | text-align: center; 108 | color: #fff; 109 | font-size: 16px; 110 | min-width: 80px; 111 | border-radius: 5px; 112 | pointer-events: none; 113 | padding: 4px 8px; 114 | z-index: 99; 115 | opacity: 0; 116 | transition: 0.15s opacity; 117 | } 118 | 119 | [data-tooltip]:hover::after, 120 | [data-tooltip]:hover::before, 121 | [data-tooltip]:focus::after, 122 | [data-tooltip]:focus::before, 123 | [data-tooltip]:active::after, 124 | [data-tooltip]:active::before { 125 | opacity: 1; 126 | } 127 | 128 | [data-tooltip]:hover, 129 | [data-tooltip]:hover, 130 | [data-tooltip]:focus, 131 | [data-tooltip]:focus, 132 | [data-tooltip]:active, 133 | [data-tooltip]:active { 134 | outline: none; 135 | } 136 | -------------------------------------------------------------------------------- /src/screens/user/components/profile.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import PropTypes from 'prop-types' 5 | import {Section, Text, Image} from '../../../shared/pattern' 6 | import UserContext from '../user-context' 7 | 8 | function Profile({user}) { 9 | return ( 10 |
11 |
12 | User Avatar 13 | {user.name} 14 | 19 | {user.login} 20 | 21 |
22 | 23 | {user.organizations.length ? ( 24 | 25 | ) : null} 26 |
27 | ) 28 | } 29 | 30 | Profile.propTypes = { 31 | user: PropTypes.shape({ 32 | avatarUrl: PropTypes.string.isRequired, 33 | name: PropTypes.string.isRequired, 34 | login: PropTypes.string.isRequired, 35 | organizations: PropTypes.array.isRequired, 36 | }).isRequired, 37 | } 38 | 39 | function ProfileStatsSection({user}) { 40 | return ( 41 |
42 | 43 | 44 | 45 |
46 | ) 47 | } 48 | 49 | ProfileStatsSection.propTypes = { 50 | user: PropTypes.shape({ 51 | followersCount: PropTypes.number, 52 | repositoriesCount: PropTypes.number, 53 | followingCount: PropTypes.number, 54 | }), 55 | } 56 | 57 | function ProfileStat({value, label}) { 58 | return ( 59 |
65 | 66 | {value} 67 | 68 | 69 | {label} 70 | 71 |
72 | ) 73 | } 74 | 75 | ProfileStat.propTypes = { 76 | value: PropTypes.number, 77 | label: PropTypes.string, 78 | } 79 | 80 | function OrganizationsSection({orgs}) { 81 | return ( 82 |
83 | Organizations 84 | {orgs.map(org => ( 85 | 86 | {`${org.login} 96 | 97 | ))} 98 |
99 | ) 100 | } 101 | 102 | OrganizationsSection.propTypes = { 103 | orgs: PropTypes.arrayOf( 104 | PropTypes.shape({ 105 | id: PropTypes.string.isRequired, 106 | avatarUrl: PropTypes.string.isRequired, 107 | login: PropTypes.string.isRequired, 108 | }), 109 | ), 110 | } 111 | 112 | function ProfileUserConsumer() { 113 | return ( 114 | 115 | {user => } 116 | 117 | ) 118 | } 119 | 120 | export default ProfileUserConsumer 121 | 122 | /* 123 | eslint 124 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 125 | react/react-in-jsx-scope: "off" 126 | */ 127 | -------------------------------------------------------------------------------- /src/github-client.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import React from 'react' 5 | import {navigate, createHistory} from '@reach/router' 6 | import netlify from 'netlify-auth-providers' 7 | import {GraphQLClient} from 'graphql-request' 8 | import {PrimaryButton} from './shared/pattern' 9 | 10 | const GitHubClientContext = React.createContext() 11 | const {Provider, Consumer} = GitHubClientContext 12 | 13 | async function authWithGitHub() { 14 | return new Promise((resolve, reject) => { 15 | var authenticator = new netlify({ 16 | site_id: process.env.REACT_APP_NETLIFY_SITE_ID, 17 | }) 18 | authenticator.authenticate( 19 | {provider: 'github', scope: 'public_repo,read:org,read:user'}, 20 | function(err, data) { 21 | if (err) { 22 | reject(err) 23 | } 24 | resolve(data) 25 | }, 26 | ) 27 | }) 28 | } 29 | 30 | const history = createHistory(window) 31 | 32 | class GitHubClientProvider extends React.Component { 33 | constructor(...args) { 34 | super(...args) 35 | this.state = {error: null} 36 | if (this.props.client) { 37 | this.state.client = this.props.client 38 | } else { 39 | const token = window.localStorage.getItem('github-token') 40 | if (token) { 41 | this.state.client = this.getClient(token) 42 | } 43 | } 44 | } 45 | componentDidMount() { 46 | if (!this.state.client) { 47 | navigate('/') 48 | } 49 | this.unsubscribeHistory = history.listen(() => { 50 | if (!this.state.client) { 51 | navigate('/') 52 | } 53 | }) 54 | } 55 | componentWillUnmount() { 56 | this.unsubscribeHistory() 57 | } 58 | getClient = token => { 59 | const headers = {Authorization: `bearer ${token}`} 60 | const client = new GraphQLClient('https://api.github.com/graphql', { 61 | headers, 62 | }) 63 | return Object.assign(client, { 64 | login: this.login, 65 | logout: this.logout, 66 | }) 67 | } 68 | logout = () => { 69 | window.localStorage.removeItem('github-token') 70 | this.setState({client: null, error: null}) 71 | navigate('/') 72 | } 73 | login = async () => { 74 | const data = await authWithGitHub().catch(error => { 75 | console.log('Oh no', error) 76 | this.setState({error}) 77 | }) 78 | window.localStorage.setItem('github-token', data.token) 79 | this.setState({client: this.getClient(data.token)}) 80 | } 81 | render() { 82 | const {client, error} = this.state 83 | const {children} = this.props 84 | 85 | return client ? ( 86 | {children} 87 | ) : ( 88 |
95 | {error ? ( 96 |
97 |

Oh no! There was an error.

98 |
{JSON.stringify(error, null, 2)}
99 |
100 | ) : ( 101 |
102 | 103 | Login with GitHub 104 | 105 |
106 | )} 107 |
108 | ) 109 | } 110 | } 111 | 112 | export { 113 | GitHubClientProvider as Provider, 114 | Consumer, 115 | GitHubClientContext as Context, 116 | } 117 | 118 | /* 119 | eslint 120 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 121 | react/react-in-jsx-scope: "off" 122 | */ 123 | -------------------------------------------------------------------------------- /src/screens/user/components/__tests__/query.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {render as rtlRender, wait} from 'react-testing-library' 3 | import * as GitHubClient from '../../../../github-client' 4 | import Query from '../query' 5 | 6 | const fakeResponse = {fakeData: {}} 7 | const fakeClient = {request: jest.fn(() => Promise.resolve(fakeResponse))} 8 | 9 | beforeEach(() => { 10 | fakeClient.request.mockClear() 11 | }) 12 | 13 | function renderQuery({ 14 | client = fakeClient, 15 | children = jest.fn(() => null), 16 | query = '', 17 | variables = {}, 18 | normalize, 19 | ...options 20 | } = {}) { 21 | const props = {query, variables, children, normalize} 22 | const utils = rtlRender( 23 | 24 | 25 | , 26 | options, 27 | ) 28 | return { 29 | ...utils, 30 | rerender: options => 31 | renderQuery({ 32 | container: utils.container, 33 | children, 34 | query, 35 | variables, 36 | normalize, 37 | ...options, 38 | }), 39 | client, 40 | query, 41 | variables, 42 | children, 43 | } 44 | } 45 | 46 | test('query makes requests to the client on mount', async () => { 47 | const {children, client, variables, query} = renderQuery() 48 | expect(children).toHaveBeenCalledTimes(2) 49 | expect(children).toHaveBeenCalledWith({ 50 | data: null, 51 | error: null, 52 | fetching: true, 53 | loaded: false, 54 | }) 55 | expect(client.request).toHaveBeenCalledTimes(1) 56 | expect(client.request).toHaveBeenCalledWith(query, variables) 57 | 58 | children.mockClear() 59 | await wait() 60 | 61 | expect(children).toHaveBeenCalledTimes(1) 62 | expect(children).toHaveBeenCalledWith({ 63 | data: fakeResponse, 64 | error: null, 65 | fetching: false, 66 | loaded: true, 67 | }) 68 | }) 69 | 70 | test('does not request if rerendered and nothing changed', async () => { 71 | const {children, client, rerender} = renderQuery() 72 | await wait() 73 | children.mockClear() 74 | client.request.mockClear() 75 | rerender() 76 | await wait() 77 | expect(client.request).toHaveBeenCalledTimes(0) 78 | expect(children).toHaveBeenCalledTimes(1) // does still re-render children. 79 | }) 80 | 81 | test('makes request if rerendered with new variables', async () => { 82 | const {client, query, rerender} = renderQuery({ 83 | variables: {username: 'fred'}, 84 | }) 85 | await wait() 86 | client.request.mockClear() 87 | const newVariables = {username: 'george'} 88 | rerender({variables: newVariables}) 89 | await wait() 90 | expect(client.request).toHaveBeenCalledTimes(1) 91 | expect(client.request).toHaveBeenCalledWith(query, newVariables) 92 | }) 93 | 94 | test('makes request if rerendered with new query', async () => { 95 | const {client, variables, rerender} = renderQuery({ 96 | query: `query neat() {}`, 97 | }) 98 | await wait() 99 | client.request.mockClear() 100 | const newQuery = `query nice() {}` 101 | rerender({query: newQuery}) 102 | await wait() 103 | expect(client.request).toHaveBeenCalledTimes(1) 104 | expect(client.request).toHaveBeenCalledWith(newQuery, variables) 105 | }) 106 | 107 | test('normalize allows modifying data', async () => { 108 | const normalize = data => ({normalizedData: data}) 109 | const {children} = renderQuery({normalize}) 110 | await wait() 111 | expect(children).toHaveBeenCalledWith({ 112 | data: {normalizedData: fakeResponse}, 113 | error: null, 114 | fetching: false, 115 | loaded: true, 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /src/screens/user/index.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import {Component} from 'react' 5 | import PropTypes from 'prop-types' 6 | import {Container, Row, Column} from '../../shared/layout' 7 | import { 8 | Text, 9 | PrimaryButton, 10 | IsolatedContainer, 11 | ButtonLink, 12 | LoadingMessagePage, 13 | } from '../../shared/pattern' 14 | import {Context as GitHubContext} from '../../github-client' 15 | import Query from './components/query' 16 | import Profile from './components/profile' 17 | import RepoFilter from './components/repo-filter' 18 | import RepoList from './components/repo-list' 19 | import UserContext from './user-context' 20 | 21 | // this allows prettier to format things without changing the string contents 22 | const gql = String.raw 23 | 24 | const userQuery = gql` 25 | query getUserData($username: String!) { 26 | user(login: $username) { 27 | name 28 | login 29 | avatarUrl 30 | followers { 31 | totalCount 32 | } 33 | following { 34 | totalCount 35 | } 36 | repositories( 37 | privacy: PUBLIC 38 | first: 100 39 | isFork: false 40 | ownerAffiliations: [COLLABORATOR, OWNER] 41 | orderBy: {field: PUSHED_AT, direction: DESC} 42 | ) { 43 | totalCount 44 | edges { 45 | node { 46 | id 47 | name 48 | description 49 | url 50 | pushedAt 51 | stargazers { 52 | totalCount 53 | } 54 | forkCount 55 | languages(first: 1) { 56 | edges { 57 | node { 58 | name 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | organizations(first: 100) { 66 | edges { 67 | node { 68 | avatarUrl 69 | id 70 | login 71 | url 72 | } 73 | } 74 | } 75 | } 76 | } 77 | ` 78 | 79 | function normalizeUserData(data) { 80 | const { 81 | user: { 82 | name, 83 | login, 84 | avatarUrl, 85 | followers: {totalCount: followersCount}, 86 | following: {totalCount: followingCount}, 87 | repositories: {totalCount: repositoriesCount, edges: reposData}, 88 | organizations: {edges: orgsData}, 89 | }, 90 | } = data 91 | const repositories = reposData.map(r => ({ 92 | ...r.node, 93 | languages: undefined, 94 | stargazersCount: r.node.stargazers.totalCount, 95 | language: r.node.languages.edges[0] 96 | ? r.node.languages.edges[0].node.name 97 | : 'Unknown', 98 | })) 99 | const organizations = orgsData.map(o => o.node) 100 | return { 101 | name, 102 | login, 103 | avatarUrl, 104 | followersCount, 105 | followingCount, 106 | repositoriesCount, 107 | repositories, 108 | organizations, 109 | } 110 | } 111 | 112 | class User extends Component { 113 | static propTypes = { 114 | username: PropTypes.string, 115 | } 116 | static contextType = GitHubContext 117 | state = {filter: ''} 118 | 119 | handleFilterUpdate = filter => { 120 | this.setState({filter}) 121 | } 122 | 123 | render() { 124 | const {username} = this.props 125 | const {filter} = this.state 126 | return ( 127 | 132 | {({fetching, data, error}) => 133 | error ? ( 134 | 135 |

There was an error loading the data

136 |
{JSON.stringify(error, null, 2)}
137 |
138 | ) : fetching ? ( 139 | Loading data for {username} 140 | ) : data ? ( 141 | 142 | 143 | 144 | 145 | 146 | 150 | Logout 151 | 152 | 153 | Try another 154 | 155 | 156 | 157 | Repositories 158 | 162 | 163 | 164 | 165 | 166 | 167 | ) : ( 168 | I have no idea what's up... 169 | ) 170 | } 171 |
172 | ) 173 | } 174 | } 175 | 176 | export default User 177 | 178 | /* 179 | eslint 180 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 181 | react/react-in-jsx-scope: "off" 182 | */ 183 | -------------------------------------------------------------------------------- /src/shared/pattern.js: -------------------------------------------------------------------------------- 1 | /* @jsx jsx */ 2 | import {jsx} from '@emotion/core' 3 | 4 | import styled from '@emotion/styled/macro' 5 | import {Link} from '@reach/router' 6 | import Loading from './loading' 7 | 8 | export const Section = styled.div( 9 | {padding: '20px 0'}, 10 | ({theme}) => theme.common.borderBottom, 11 | ) 12 | 13 | const heading = { 14 | display: 'block', 15 | fontFamily: 'inherit', 16 | fontWeight: '500', 17 | lineHeight: '1.1', 18 | } 19 | const largerHeading = { 20 | marginTop: '20px', 21 | marginBottom: '10px', 22 | } 23 | 24 | const smallerHeading = { 25 | marginTop: '10px', 26 | marginBottom: '10px', 27 | } 28 | 29 | export const Text = styled.span( 30 | variantStyles({ 31 | tint: { 32 | faded: ({theme}) => ({color: theme.colors.faded}), 33 | fadedExtra: ({theme}) => ({color: theme.colors.fadedExtra}), 34 | }, 35 | size: { 36 | superheading: [heading, largerHeading, {fontSize: 36}], 37 | heading: [heading, largerHeading, {fontSize: 30}], 38 | subheading: [heading, largerHeading, {fontSize: 24}], 39 | superstandard: [heading, smallerHeading, {fontSize: 18}], 40 | standard: [heading, smallerHeading, {fontSize: 14}], 41 | substandard: [heading, smallerHeading, {fontSize: 12}], 42 | }, 43 | }), 44 | ) 45 | 46 | export const Input = styled.input({ 47 | display: 'block', 48 | width: '100%', 49 | height: '34px', 50 | padding: '6px 12px', 51 | fontSize: '14px', 52 | lineHeight: '1.42857143', 53 | color: '#555', 54 | backgroundColor: '#fff', 55 | backgroundImage: 'none', 56 | border: '1px solid #ccc', 57 | borderRadius: '4px', 58 | boxShadow: 'inset 0 1px 1px rgba(0, 0, 0, .075)', 59 | transition: 'border-color ease-in-out .15s, box-shadow ease-in-out .15s', 60 | ':focus': { 61 | borderColor: '#66afe9', 62 | outline: '0', 63 | boxShadow: 64 | 'inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6)', 65 | }, 66 | }) 67 | 68 | export const Button = styled.button({ 69 | display: 'inline-block', 70 | padding: '6px 12px', 71 | marginBottom: '0', 72 | fontSize: '14px', 73 | fontWeight: 'normal', 74 | lineHeight: '1.42857143', 75 | textAlign: 'center', 76 | whiteSpace: 'nowrap', 77 | verticalAlign: 'middle', 78 | touchAction: 'manipulation', 79 | cursor: 'pointer', 80 | userSelect: 'none', 81 | backgroundImage: 'none', 82 | border: '1px solid transparent', 83 | borderRadius: '4px', 84 | textDecoration: 'none', 85 | color: '#333', 86 | }) 87 | 88 | export const PrimaryButton = styled(Button)({ 89 | color: '#fff', 90 | backgroundColor: '#337ab7', 91 | borderColor: '#2e6da4', 92 | '&:hover,&:active,&:focus': { 93 | color: '#fff', 94 | backgroundColor: '#286090', 95 | borderColor: '#204d74', 96 | }, 97 | ':focus': { 98 | borderColor: '#122b40', 99 | }, 100 | }) 101 | 102 | export const ButtonLink = Button.withComponent(Link) 103 | export const PrimaryButtonLink = PrimaryButton.withComponent(Link) 104 | 105 | export const Image = styled.img( 106 | {border: '0', verticalAlign: 'middle'}, 107 | propStyles({ 108 | responsive: { 109 | display: 'block', 110 | maxWidth: '100%', 111 | height: 'auto', 112 | }, 113 | rounded: { 114 | borderRadius: '6px', 115 | }, 116 | }), 117 | ) 118 | 119 | export const Anchor = styled.a({ 120 | color: '#337ab7', 121 | textDecoration: 'none', 122 | '&:active,&:hover': {outline: 0}, 123 | '&:hover,&:focus': { 124 | color: '#23527c', 125 | textDecoration: 'underline', 126 | }, 127 | ':focus': { 128 | outline: '5px auto -webkit-focus-ring-color', 129 | outlineOffset: '-2px', 130 | }, 131 | }) 132 | 133 | export function IsolatedContainer({children, ...props}) { 134 | return ( 135 |
143 |
{children}
144 |
145 | ) 146 | } 147 | 148 | export function LoadingMessagePage({children}) { 149 | return ( 150 | 151 |
152 |

153 | {children} 154 |

155 | 156 |
157 |
158 | ) 159 | } 160 | 161 | /** 162 | * Makes it easier to create an emotion component which 163 | * accepts props to enable/disable certain styles. 164 | * 165 | * accepts an object where the key is a prop and the value 166 | * is the styles that should be applied if that prop is 167 | * passed. Returns a function which you pass to a 168 | * emotionComponentFactory. 169 | * 170 | * @param {Object} styles The prop to styles object 171 | * @return {Function} the dynamic styles function 172 | */ 173 | function propStyles(styles) { 174 | return function dynamicStyles(props) { 175 | return Object.keys(props).map(key => { 176 | if (styles[key]) { 177 | return applyStyles(styles[key], props) 178 | } 179 | return null 180 | }) 181 | } 182 | } 183 | 184 | /** 185 | * Makes it easier to create an emotion component 186 | * which accepts enums for certain variants of styles 187 | * 188 | * Accepts an object where the key is a variant name 189 | * (the prop consumers will use) and the value is an 190 | * object where those keys are the possible values for 191 | * the variant prop, and the value is the styles to be 192 | * applied. 193 | */ 194 | function variantStyles(styles) { 195 | return function dynamicStyles(props) { 196 | return Object.entries(props).map(([key, value]) => { 197 | if (styles[key]) { 198 | return applyStyles(styles[key][value], props) 199 | } 200 | return null 201 | }) 202 | } 203 | } 204 | 205 | function applyStyles(styles, props) { 206 | return typeof styles === 'function' 207 | ? styles(props) 208 | : Array.isArray(styles) 209 | ? styles.map(s => applyStyles(s, props)) 210 | : styles 211 | } 212 | 213 | /* 214 | eslint 215 | no-unused-vars: ["warn", {"varsIgnorePattern": "(jsx)"}] 216 | react/react-in-jsx-scope: "off" 217 | */ 218 | -------------------------------------------------------------------------------- /src/docs/mock-query-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": { 3 | "name": "Kent C. Dodds", 4 | "login": "kentcdodds", 5 | "avatarUrl": "https://avatars0.githubusercontent.com/u/1500684?v=4", 6 | "followers": { 7 | "totalCount": 7046 8 | }, 9 | "following": { 10 | "totalCount": 38 11 | }, 12 | "repositories": { 13 | "totalCount": 515, 14 | "edges": [ 15 | { 16 | "node": { 17 | "id": "MDEwOlJlcG9zaXRvcnk5ODMxNTczNQ==", 18 | "name": "downshift", 19 | "description": "🏎 Primitive to build simple, flexible, WAI-ARIA compliant enhanced input React components", 20 | "url": "https://github.com/paypal/downshift", 21 | "pushedAt": "2018-11-08T19:49:21Z", 22 | "stargazers": { 23 | "totalCount": 5794 24 | }, 25 | "forkCount": 422, 26 | "languages": { 27 | "edges": [ 28 | { 29 | "node": { 30 | "name": "JavaScript" 31 | } 32 | } 33 | ] 34 | } 35 | } 36 | }, 37 | { 38 | "node": { 39 | "id": "MDEwOlJlcG9zaXRvcnkxMjg0MDU5MTU=", 40 | "name": "cypress-testing-library", 41 | "description": "🐅 Simple and complete custom Cypress commands and utilities that encourage good testing practices.", 42 | "url": "https://github.com/kentcdodds/cypress-testing-library", 43 | "pushedAt": "2018-11-08T19:44:09Z", 44 | "stargazers": { 45 | "totalCount": 264 46 | }, 47 | "forkCount": 18, 48 | "languages": { 49 | "edges": [ 50 | { 51 | "node": { 52 | "name": "JavaScript" 53 | } 54 | } 55 | ] 56 | } 57 | } 58 | }, 59 | { 60 | "node": { 61 | "id": "MDEwOlJlcG9zaXRvcnk3MjI2Mzg2MQ==", 62 | "name": "react-github-profile", 63 | "description": null, 64 | "url": "https://github.com/kentcdodds/react-github-profile", 65 | "pushedAt": "2018-11-08T19:05:53Z", 66 | "stargazers": { 67 | "totalCount": 46 68 | }, 69 | "forkCount": 3, 70 | "languages": { 71 | "edges": [ 72 | { 73 | "node": { 74 | "name": "HTML" 75 | } 76 | } 77 | ] 78 | } 79 | } 80 | }, 81 | { 82 | "node": { 83 | "id": "MDEwOlJlcG9zaXRvcnk4OTI5MDYwMQ==", 84 | "name": "theming", 85 | "description": "Unified CSSinJS theming solution for React", 86 | "url": "https://github.com/cssinjs/theming", 87 | "pushedAt": "2018-11-08T12:37:01Z", 88 | "stargazers": { 89 | "totalCount": 233 90 | }, 91 | "forkCount": 29, 92 | "languages": { 93 | "edges": [ 94 | { 95 | "node": { 96 | "name": "JavaScript" 97 | } 98 | } 99 | ] 100 | } 101 | } 102 | }, 103 | { 104 | "node": { 105 | "id": "MDEwOlJlcG9zaXRvcnkxNTY2NDEzNzA=", 106 | "name": "babel-blade", 107 | "description": "a Babel plugin/macro that solves the Double Declaration problem in GraphQL. Please see Docs at:", 108 | "url": "https://github.com/kentcdodds/babel-blade", 109 | "pushedAt": "2018-11-08T02:54:26Z", 110 | "stargazers": { 111 | "totalCount": 0 112 | }, 113 | "forkCount": 0, 114 | "languages": { 115 | "edges": [ 116 | { 117 | "node": { 118 | "name": "JavaScript" 119 | } 120 | } 121 | ] 122 | } 123 | } 124 | }, 125 | { 126 | "node": { 127 | "id": "MDEwOlJlcG9zaXRvcnk1MjcxMDA2NQ==", 128 | "name": "all-contributors", 129 | "description": "✨ Recognize all contributors, not just the ones who push code ✨", 130 | "url": "https://github.com/kentcdodds/all-contributors", 131 | "pushedAt": "2018-11-07T20:00:55Z", 132 | "stargazers": { 133 | "totalCount": 2077 134 | }, 135 | "forkCount": 294, 136 | "languages": { 137 | "edges": [ 138 | { 139 | "node": { 140 | "name": "JavaScript" 141 | } 142 | } 143 | ] 144 | } 145 | } 146 | }, 147 | { 148 | "node": { 149 | "id": "MDEwOlJlcG9zaXRvcnkxMjgzOTYwNzY=", 150 | "name": "dom-testing-library", 151 | "description": "🐙 Simple and complete DOM testing utilities that encourage good testing practices.", 152 | "url": "https://github.com/kentcdodds/dom-testing-library", 153 | "pushedAt": "2018-11-07T18:21:49Z", 154 | "stargazers": { 155 | "totalCount": 739 156 | }, 157 | "forkCount": 83, 158 | "languages": { 159 | "edges": [ 160 | { 161 | "node": { 162 | "name": "JavaScript" 163 | } 164 | } 165 | ] 166 | } 167 | } 168 | }, 169 | { 170 | "node": { 171 | "id": "MDEwOlJlcG9zaXRvcnkxMjU4NjM1MDc=", 172 | "name": "react-testing-library", 173 | "description": "🐐 Simple and complete React DOM testing utilities that encourage good testing practices.", 174 | "url": "https://github.com/kentcdodds/react-testing-library", 175 | "pushedAt": "2018-11-06T22:13:45Z", 176 | "stargazers": { 177 | "totalCount": 3775 178 | }, 179 | "forkCount": 222, 180 | "languages": { 181 | "edges": [ 182 | { 183 | "node": { 184 | "name": "JavaScript" 185 | } 186 | } 187 | ] 188 | } 189 | } 190 | }, 191 | { 192 | "node": { 193 | "id": "MDEwOlJlcG9zaXRvcnk5MTQ3NzkwMw==", 194 | "name": "babel-plugin-tester", 195 | "description": "Utilities for testing babel plugins", 196 | "url": "https://github.com/babel-utils/babel-plugin-tester", 197 | "pushedAt": "2018-11-06T21:49:37Z", 198 | "stargazers": { 199 | "totalCount": 114 200 | }, 201 | "forkCount": 20, 202 | "languages": { 203 | "edges": [ 204 | { 205 | "node": { 206 | "name": "JavaScript" 207 | } 208 | } 209 | ] 210 | } 211 | } 212 | }, 213 | { 214 | "node": { 215 | "id": "MDEwOlJlcG9zaXRvcnk1Mjc0MjA0Mw==", 216 | "name": "all-contributors-cli", 217 | "description": "Tool to help automate adding contributor acknowledgements according to the all-contributors specification.", 218 | "url": "https://github.com/jfmengels/all-contributors-cli", 219 | "pushedAt": "2018-11-06T21:44:54Z", 220 | "stargazers": { 221 | "totalCount": 204 222 | }, 223 | "forkCount": 60, 224 | "languages": { 225 | "edges": [ 226 | { 227 | "node": { 228 | "name": "JavaScript" 229 | } 230 | } 231 | ] 232 | } 233 | } 234 | }, 235 | { 236 | "node": { 237 | "id": "MDEwOlJlcG9zaXRvcnkxMDc3ODc2NQ==", 238 | "name": "kentcdodds.com", 239 | "description": "Kents Homepage", 240 | "url": "https://github.com/kentcdodds/kentcdodds.com", 241 | "pushedAt": "2018-11-06T19:46:39Z", 242 | "stargazers": { 243 | "totalCount": 13 244 | }, 245 | "forkCount": 5, 246 | "languages": { 247 | "edges": [ 248 | { 249 | "node": { 250 | "name": "JavaScript" 251 | } 252 | } 253 | ] 254 | } 255 | } 256 | }, 257 | { 258 | "node": { 259 | "id": "MDEwOlJlcG9zaXRvcnkxMjg3Nzg2OTI=", 260 | "name": "jest-dom", 261 | "description": ":owl: Custom jest matchers to test the state of the DOM", 262 | "url": "https://github.com/gnapse/jest-dom", 263 | "pushedAt": "2018-11-06T13:55:42Z", 264 | "stargazers": { 265 | "totalCount": 188 266 | }, 267 | "forkCount": 18, 268 | "languages": { 269 | "edges": [ 270 | { 271 | "node": { 272 | "name": "JavaScript" 273 | } 274 | } 275 | ] 276 | } 277 | } 278 | }, 279 | { 280 | "node": { 281 | "id": "MDEwOlJlcG9zaXRvcnkzNTcxMTU5NQ==", 282 | "name": "NG6-starter", 283 | "description": ":ng: An AngularJS Starter repo for AngularJS + ES6 + Webpack by @AngularClass a @OneSpeed-io company", 284 | "url": "https://github.com/gdi2290/NG6-starter", 285 | "pushedAt": "2018-11-04T23:35:20Z", 286 | "stargazers": { 287 | "totalCount": 1945 288 | }, 289 | "forkCount": 1561, 290 | "languages": { 291 | "edges": [ 292 | { 293 | "node": { 294 | "name": "JavaScript" 295 | } 296 | } 297 | ] 298 | } 299 | } 300 | }, 301 | { 302 | "node": { 303 | "id": "MDEwOlJlcG9zaXRvcnk4NzEyMzE0Ng==", 304 | "name": "glamorous", 305 | "description": "💄 Maintainable CSS with React", 306 | "url": "https://github.com/paypal/glamorous", 307 | "pushedAt": "2018-11-03T23:55:38Z", 308 | "stargazers": { 309 | "totalCount": 3751 310 | }, 311 | "forkCount": 282, 312 | "languages": { 313 | "edges": [ 314 | { 315 | "node": { 316 | "name": "JavaScript" 317 | } 318 | } 319 | ] 320 | } 321 | } 322 | }, 323 | { 324 | "node": { 325 | "id": "MDEwOlJlcG9zaXRvcnk0MzUxMjkxNA==", 326 | "name": "cross-env", 327 | "description": "🔀 Cross platform setting of environment scripts", 328 | "url": "https://github.com/kentcdodds/cross-env", 329 | "pushedAt": "2018-11-03T23:54:03Z", 330 | "stargazers": { 331 | "totalCount": 3123 332 | }, 333 | "forkCount": 139, 334 | "languages": { 335 | "edges": [ 336 | { 337 | "node": { 338 | "name": "JavaScript" 339 | } 340 | } 341 | ] 342 | } 343 | } 344 | }, 345 | { 346 | "node": { 347 | "id": "MDEwOlJlcG9zaXRvcnk3ODg5MDczMQ==", 348 | "name": "prettier-eslint-cli", 349 | "description": "CLI for prettier-eslint", 350 | "url": "https://github.com/prettier/prettier-eslint-cli", 351 | "pushedAt": "2018-11-03T17:20:24Z", 352 | "stargazers": { 353 | "totalCount": 298 354 | }, 355 | "forkCount": 52, 356 | "languages": { 357 | "edges": [ 358 | { 359 | "node": { 360 | "name": "JavaScript" 361 | } 362 | } 363 | ] 364 | } 365 | } 366 | }, 367 | { 368 | "node": { 369 | "id": "MDEwOlJlcG9zaXRvcnk0MjI1OTYxOA==", 370 | "name": "node-manage-path", 371 | "description": "This micro-lib allows you to alter the $PATH in a cross-platform way.", 372 | "url": "https://github.com/kentcdodds/node-manage-path", 373 | "pushedAt": "2018-11-03T14:28:27Z", 374 | "stargazers": { 375 | "totalCount": 3 376 | }, 377 | "forkCount": 0, 378 | "languages": { 379 | "edges": [ 380 | { 381 | "node": { 382 | "name": "JavaScript" 383 | } 384 | } 385 | ] 386 | } 387 | } 388 | }, 389 | { 390 | "node": { 391 | "id": "MDEwOlJlcG9zaXRvcnkyMTQ3Njc0Ng==", 392 | "name": "nz-toggle", 393 | "description": "Dual and triple-state toggles for AngularJS", 394 | "url": "https://github.com/tannerlinsley/nz-toggle", 395 | "pushedAt": "2018-10-31T13:45:17Z", 396 | "stargazers": { 397 | "totalCount": 36 398 | }, 399 | "forkCount": 26, 400 | "languages": { 401 | "edges": [ 402 | { 403 | "node": { 404 | "name": "CSS" 405 | } 406 | } 407 | ] 408 | } 409 | } 410 | }, 411 | { 412 | "node": { 413 | "id": "MDEwOlJlcG9zaXRvcnk2NDY5OTQ5NA==", 414 | "name": "awesome-webpack", 415 | "description": "A curated list of awesome Webpack resources, libraries and tools", 416 | "url": "https://github.com/webpack-contrib/awesome-webpack", 417 | "pushedAt": "2018-10-31T11:09:42Z", 418 | "stargazers": { 419 | "totalCount": 2907 420 | }, 421 | "forkCount": 224, 422 | "languages": { 423 | "edges": [ 424 | { 425 | "node": { 426 | "name": "HTML" 427 | } 428 | } 429 | ] 430 | } 431 | } 432 | }, 433 | { 434 | "node": { 435 | "id": "MDEwOlJlcG9zaXRvcnkzNTMxMjQ1OA==", 436 | "name": "nyc", 437 | "description": "the Istanbul command line interface", 438 | "url": "https://github.com/istanbuljs/nyc", 439 | "pushedAt": "2018-10-31T06:28:47Z", 440 | "stargazers": { 441 | "totalCount": 2993 442 | }, 443 | "forkCount": 222, 444 | "languages": { 445 | "edges": [ 446 | { 447 | "node": { 448 | "name": "JavaScript" 449 | } 450 | } 451 | ] 452 | } 453 | } 454 | }, 455 | { 456 | "node": { 457 | "id": "MDEwOlJlcG9zaXRvcnk0MDQ1MDY3NQ==", 458 | "name": "firsttimersonly", 459 | "description": "The Repository for the FirstTimersOnly movement in Open Source. We want projects to reserve some issues for newbies.", 460 | "url": "https://github.com/shanselman/firsttimersonly", 461 | "pushedAt": "2018-10-30T15:13:40Z", 462 | "stargazers": { 463 | "totalCount": 251 464 | }, 465 | "forkCount": 177, 466 | "languages": { 467 | "edges": [ 468 | { 469 | "node": { 470 | "name": "Ruby" 471 | } 472 | } 473 | ] 474 | } 475 | } 476 | }, 477 | { 478 | "node": { 479 | "id": "MDEwOlJlcG9zaXRvcnkxMDIwMjk2MTI=", 480 | "name": "react-toggled", 481 | "description": "Component to build simple, flexible, and accessible toggle components", 482 | "url": "https://github.com/kentcdodds/react-toggled", 483 | "pushedAt": "2018-10-25T21:50:39Z", 484 | "stargazers": { 485 | "totalCount": 374 486 | }, 487 | "forkCount": 34, 488 | "languages": { 489 | "edges": [ 490 | { 491 | "node": { 492 | "name": "JavaScript" 493 | } 494 | } 495 | ] 496 | } 497 | } 498 | }, 499 | { 500 | "node": { 501 | "id": "MDEwOlJlcG9zaXRvcnkxNTQ3MDcwMTM=", 502 | "name": "react-hooks-and-suspense-egghead-playlist", 503 | "description": "This is the code for the egghead playlist \"React Hooks and Suspense\"", 504 | "url": "https://github.com/kentcdodds/react-hooks-and-suspense-egghead-playlist", 505 | "pushedAt": "2018-10-25T17:52:56Z", 506 | "stargazers": { 507 | "totalCount": 85 508 | }, 509 | "forkCount": 4, 510 | "languages": { 511 | "edges": [ 512 | { 513 | "node": { 514 | "name": "HTML" 515 | } 516 | } 517 | ] 518 | } 519 | } 520 | }, 521 | { 522 | "node": { 523 | "id": "MDEwOlJlcG9zaXRvcnk3NzY1NTAxOA==", 524 | "name": "testing-workshop", 525 | "description": "A workshop for learning how to test JavaScript applications", 526 | "url": "https://github.com/kentcdodds/testing-workshop", 527 | "pushedAt": "2018-10-25T15:46:48Z", 528 | "stargazers": { 529 | "totalCount": 737 530 | }, 531 | "forkCount": 308, 532 | "languages": { 533 | "edges": [ 534 | { 535 | "node": { 536 | "name": "JavaScript" 537 | } 538 | } 539 | ] 540 | } 541 | } 542 | }, 543 | { 544 | "node": { 545 | "id": "MDEwOlJlcG9zaXRvcnkxNTQ2MDA5NzU=", 546 | "name": "suspense-max-duration-issue", 547 | "description": null, 548 | "url": "https://github.com/kentcdodds/suspense-max-duration-issue", 549 | "pushedAt": "2018-10-25T04:15:10Z", 550 | "stargazers": { 551 | "totalCount": 2 552 | }, 553 | "forkCount": 2, 554 | "languages": { 555 | "edges": [ 556 | { 557 | "node": { 558 | "name": "HTML" 559 | } 560 | } 561 | ] 562 | } 563 | } 564 | }, 565 | { 566 | "node": { 567 | "id": "MDEwOlJlcG9zaXRvcnkxNDgyNDIxMjI=", 568 | "name": "static-testing-tools", 569 | "description": null, 570 | "url": "https://github.com/kentcdodds/static-testing-tools", 571 | "pushedAt": "2018-10-24T15:58:04Z", 572 | "stargazers": { 573 | "totalCount": 18 574 | }, 575 | "forkCount": 2, 576 | "languages": { 577 | "edges": [ 578 | { 579 | "node": { 580 | "name": "JavaScript" 581 | } 582 | } 583 | ] 584 | } 585 | } 586 | }, 587 | { 588 | "node": { 589 | "id": "MDEwOlJlcG9zaXRvcnkxMDM4NjQ4Njg=", 590 | "name": "import-all.macro", 591 | "description": "A babel-macro that allows you to import all files that match a glob", 592 | "url": "https://github.com/kentcdodds/import-all.macro", 593 | "pushedAt": "2018-10-23T22:23:28Z", 594 | "stargazers": { 595 | "totalCount": 73 596 | }, 597 | "forkCount": 9, 598 | "languages": { 599 | "edges": [ 600 | { 601 | "node": { 602 | "name": "JavaScript" 603 | } 604 | } 605 | ] 606 | } 607 | } 608 | }, 609 | { 610 | "node": { 611 | "id": "MDEwOlJlcG9zaXRvcnk0MTcxODE4NQ==", 612 | "name": "awesome-angular", 613 | "description": ":page_facing_up: A curated list of awesome Angular resources by @TipeIO", 614 | "url": "https://github.com/gdi2290/awesome-angular", 615 | "pushedAt": "2018-10-23T07:08:18Z", 616 | "stargazers": { 617 | "totalCount": 6078 618 | }, 619 | "forkCount": 917, 620 | "languages": { 621 | "edges": [ 622 | { 623 | "node": { 624 | "name": "HTML" 625 | } 626 | } 627 | ] 628 | } 629 | } 630 | }, 631 | { 632 | "node": { 633 | "id": "MDEwOlJlcG9zaXRvcnkxNDM0ODI1MjA=", 634 | "name": "simply-react", 635 | "description": null, 636 | "url": "https://github.com/kentcdodds/simply-react", 637 | "pushedAt": "2018-10-20T13:07:13Z", 638 | "stargazers": { 639 | "totalCount": 60 640 | }, 641 | "forkCount": 2, 642 | "languages": { 643 | "edges": [ 644 | { 645 | "node": { 646 | "name": "JavaScript" 647 | } 648 | } 649 | ] 650 | } 651 | } 652 | }, 653 | { 654 | "node": { 655 | "id": "MDEwOlJlcG9zaXRvcnkxMzcwMjAzMTI=", 656 | "name": "js-testing-fundamentals", 657 | "description": null, 658 | "url": "https://github.com/kentcdodds/js-testing-fundamentals", 659 | "pushedAt": "2018-10-19T14:41:50Z", 660 | "stargazers": { 661 | "totalCount": 93 662 | }, 663 | "forkCount": 10, 664 | "languages": { 665 | "edges": [ 666 | { 667 | "node": { 668 | "name": "JavaScript" 669 | } 670 | } 671 | ] 672 | } 673 | } 674 | }, 675 | { 676 | "node": { 677 | "id": "MDEwOlJlcG9zaXRvcnkxNDEzMTM4MjY=", 678 | "name": "setup-prettier", 679 | "description": null, 680 | "url": "https://github.com/kentcdodds/setup-prettier", 681 | "pushedAt": "2018-10-18T13:36:55Z", 682 | "stargazers": { 683 | "totalCount": 37 684 | }, 685 | "forkCount": 3, 686 | "languages": { 687 | "edges": [ 688 | { 689 | "node": { 690 | "name": "JavaScript" 691 | } 692 | } 693 | ] 694 | } 695 | } 696 | }, 697 | { 698 | "node": { 699 | "id": "MDEwOlJlcG9zaXRvcnk5NjQ4NDAzMQ==", 700 | "name": "babel-plugin-macros", 701 | "description": "🎣 Enables zero-config, importable babel plugins", 702 | "url": "https://github.com/kentcdodds/babel-plugin-macros", 703 | "pushedAt": "2018-10-18T09:05:33Z", 704 | "stargazers": { 705 | "totalCount": 908 706 | }, 707 | "forkCount": 63, 708 | "languages": { 709 | "edges": [ 710 | { 711 | "node": { 712 | "name": "JavaScript" 713 | } 714 | } 715 | ] 716 | } 717 | } 718 | }, 719 | { 720 | "node": { 721 | "id": "MDEwOlJlcG9zaXRvcnkzMTgyOTc3MA==", 722 | "name": "angular-starter", 723 | "description": ":tada: An Angular Starter kit featuring Angular (Router, Http, Forms, Services, Tests, E2E, Dev/Prod, HMR, Async/Lazy Routes, AoT via ngc), Karma, Protractor, Jasmine, Istanbul, TypeScript, TsLint, Codelyzer, Hot Module Replacement, @types, and Webpack by @TipeIO", 724 | "url": "https://github.com/gdi2290/angular-starter", 725 | "pushedAt": "2018-10-18T01:39:44Z", 726 | "stargazers": { 727 | "totalCount": 10108 728 | }, 729 | "forkCount": 5476, 730 | "languages": { 731 | "edges": [ 732 | { 733 | "node": { 734 | "name": "HTML" 735 | } 736 | } 737 | ] 738 | } 739 | } 740 | }, 741 | { 742 | "node": { 743 | "id": "MDEwOlJlcG9zaXRvcnkxMzgyNTc4Nzg=", 744 | "name": "jest-cypress-react-babel-webpack", 745 | "description": "See how to configure Jest and Cypress with React, Babel, and Webpack", 746 | "url": "https://github.com/kentcdodds/jest-cypress-react-babel-webpack", 747 | "pushedAt": "2018-10-16T15:22:29Z", 748 | "stargazers": { 749 | "totalCount": 76 750 | }, 751 | "forkCount": 15, 752 | "languages": { 753 | "edges": [ 754 | { 755 | "node": { 756 | "name": "JavaScript" 757 | } 758 | } 759 | ] 760 | } 761 | } 762 | }, 763 | { 764 | "node": { 765 | "id": "MDEwOlJlcG9zaXRvcnkxMjc3NTQ3Njg=", 766 | "name": "advanced-react-patterns-v2", 767 | "description": "Created with CodeSandbox", 768 | "url": "https://github.com/kentcdodds/advanced-react-patterns-v2", 769 | "pushedAt": "2018-10-14T20:40:08Z", 770 | "stargazers": { 771 | "totalCount": 955 772 | }, 773 | "forkCount": 320, 774 | "languages": { 775 | "edges": [ 776 | { 777 | "node": { 778 | "name": "HTML" 779 | } 780 | } 781 | ] 782 | } 783 | } 784 | }, 785 | { 786 | "node": { 787 | "id": "MDEwOlJlcG9zaXRvcnkxMTQyNzE1MDU=", 788 | "name": "awesome-react-render-props", 789 | "description": "Awesome list of React components with render props", 790 | "url": "https://github.com/jaredpalmer/awesome-react-render-props", 791 | "pushedAt": "2018-10-14T01:30:08Z", 792 | "stargazers": { 793 | "totalCount": 1042 794 | }, 795 | "forkCount": 77, 796 | "languages": { 797 | "edges": [] 798 | } 799 | } 800 | }, 801 | { 802 | "node": { 803 | "id": "MDEwOlJlcG9zaXRvcnk3Mzk0OTQ4Ng==", 804 | "name": "rtl-css-js", 805 | "description": "RTL for CSS in JS", 806 | "url": "https://github.com/kentcdodds/rtl-css-js", 807 | "pushedAt": "2018-10-11T18:45:46Z", 808 | "stargazers": { 809 | "totalCount": 63 810 | }, 811 | "forkCount": 15, 812 | "languages": { 813 | "edges": [ 814 | { 815 | "node": { 816 | "name": "JavaScript" 817 | } 818 | } 819 | ] 820 | } 821 | } 822 | }, 823 | { 824 | "node": { 825 | "id": "MDEwOlJlcG9zaXRvcnkxMDIyNDQ3MjI=", 826 | "name": "kcd-scripts", 827 | "description": "CLI toolbox for common scripts for my projects", 828 | "url": "https://github.com/kentcdodds/kcd-scripts", 829 | "pushedAt": "2018-10-11T16:49:11Z", 830 | "stargazers": { 831 | "totalCount": 340 832 | }, 833 | "forkCount": 81, 834 | "languages": { 835 | "edges": [ 836 | { 837 | "node": { 838 | "name": "JavaScript" 839 | } 840 | } 841 | ] 842 | } 843 | } 844 | }, 845 | { 846 | "node": { 847 | "id": "MDEwOlJlcG9zaXRvcnk4OTY0MDQ4MQ==", 848 | "name": "css-in-js-precompiler", 849 | "description": "WORK IN PROGRESS: Precompiles CSS-in-JS objects to CSS strings", 850 | "url": "https://github.com/kentcdodds/css-in-js-precompiler", 851 | "pushedAt": "2018-10-09T18:58:55Z", 852 | "stargazers": { 853 | "totalCount": 72 854 | }, 855 | "forkCount": 3, 856 | "languages": { 857 | "edges": [ 858 | { 859 | "node": { 860 | "name": "JavaScript" 861 | } 862 | } 863 | ] 864 | } 865 | } 866 | }, 867 | { 868 | "node": { 869 | "id": "MDEwOlJlcG9zaXRvcnk5NzI1OTU5OQ==", 870 | "name": "next.js", 871 | "description": "Framework for server-rendered React apps", 872 | "url": "https://github.com/kentcdodds/next.js", 873 | "pushedAt": "2018-10-09T14:59:32Z", 874 | "stargazers": { 875 | "totalCount": 0 876 | }, 877 | "forkCount": 0, 878 | "languages": { 879 | "edges": [ 880 | { 881 | "node": { 882 | "name": "JavaScript" 883 | } 884 | } 885 | ] 886 | } 887 | } 888 | }, 889 | { 890 | "node": { 891 | "id": "MDEwOlJlcG9zaXRvcnk3NzY1NTA0OQ==", 892 | "name": "asts-workshop", 893 | "description": "Improved productivity 💯 with the practical 🤓 use of the power 💪 of Abstract Syntax Trees 🌳 to lint ⚠️ and transform 🔀 your code", 894 | "url": "https://github.com/kentcdodds/asts-workshop", 895 | "pushedAt": "2018-10-07T15:26:54Z", 896 | "stargazers": { 897 | "totalCount": 134 898 | }, 899 | "forkCount": 25, 900 | "languages": { 901 | "edges": [ 902 | { 903 | "node": { 904 | "name": "JavaScript" 905 | } 906 | } 907 | ] 908 | } 909 | } 910 | }, 911 | { 912 | "node": { 913 | "id": "MDEwOlJlcG9zaXRvcnk1NzUyMTg2Mg==", 914 | "name": "generator-kcd-oss", 915 | "description": "A yeoman generator for my open source modules", 916 | "url": "https://github.com/kentcdodds/generator-kcd-oss", 917 | "pushedAt": "2018-10-07T01:36:40Z", 918 | "stargazers": { 919 | "totalCount": 59 920 | }, 921 | "forkCount": 15, 922 | "languages": { 923 | "edges": [ 924 | { 925 | "node": { 926 | "name": "JavaScript" 927 | } 928 | } 929 | ] 930 | } 931 | } 932 | }, 933 | { 934 | "node": { 935 | "id": "MDEwOlJlcG9zaXRvcnkxNTE2NDM4MjQ=", 936 | "name": "reach-ui", 937 | "description": "The Accessible Foundation for React Apps and Design Systems.", 938 | "url": "https://github.com/kentcdodds/reach-ui", 939 | "pushedAt": "2018-10-05T22:09:16Z", 940 | "stargazers": { 941 | "totalCount": 0 942 | }, 943 | "forkCount": 0, 944 | "languages": { 945 | "edges": [ 946 | { 947 | "node": { 948 | "name": "JavaScript" 949 | } 950 | } 951 | ] 952 | } 953 | } 954 | }, 955 | { 956 | "node": { 957 | "id": "MDEwOlJlcG9zaXRvcnkzOTc3NDQ4OQ==", 958 | "name": "up-for-grabs.net", 959 | "description": "Jump in!", 960 | "url": "https://github.com/kentcdodds/up-for-grabs.net", 961 | "pushedAt": "2018-10-05T16:19:13Z", 962 | "stargazers": { 963 | "totalCount": 0 964 | }, 965 | "forkCount": 0, 966 | "languages": { 967 | "edges": [ 968 | { 969 | "node": { 970 | "name": "Ruby" 971 | } 972 | } 973 | ] 974 | } 975 | } 976 | }, 977 | { 978 | "node": { 979 | "id": "MDEwOlJlcG9zaXRvcnk3ODgxOTE5MA==", 980 | "name": "prettier-eslint", 981 | "description": "Code :arrow_right: prettier :arrow_right: eslint --fix :arrow_right: Formatted Code :sparkles:", 982 | "url": "https://github.com/prettier/prettier-eslint", 983 | "pushedAt": "2018-10-05T13:52:32Z", 984 | "stargazers": { 985 | "totalCount": 1974 986 | }, 987 | "forkCount": 116, 988 | "languages": { 989 | "edges": [ 990 | { 991 | "node": { 992 | "name": "JavaScript" 993 | } 994 | } 995 | ] 996 | } 997 | } 998 | }, 999 | { 1000 | "node": { 1001 | "id": "MDEwOlJlcG9zaXRvcnkxNDg5MTQyNDc=", 1002 | "name": "dom-testing-library-with-anything", 1003 | "description": "you can use dom-testing-library with anything that renders to the DOM", 1004 | "url": "https://github.com/kentcdodds/dom-testing-library-with-anything", 1005 | "pushedAt": "2018-10-04T21:30:34Z", 1006 | "stargazers": { 1007 | "totalCount": 110 1008 | }, 1009 | "forkCount": 20, 1010 | "languages": { 1011 | "edges": [ 1012 | { 1013 | "node": { 1014 | "name": "JavaScript" 1015 | } 1016 | } 1017 | ] 1018 | } 1019 | } 1020 | }, 1021 | { 1022 | "node": { 1023 | "id": "MDEwOlJlcG9zaXRvcnk1NzA3Mjk3MA==", 1024 | "name": "nps", 1025 | "description": ":100: All the benefits of npm scripts without the cost of a bloated package.json and limits of json :sparkles:", 1026 | "url": "https://github.com/kentcdodds/nps", 1027 | "pushedAt": "2018-10-04T16:48:49Z", 1028 | "stargazers": { 1029 | "totalCount": 954 1030 | }, 1031 | "forkCount": 86, 1032 | "languages": { 1033 | "edges": [ 1034 | { 1035 | "node": { 1036 | "name": "JavaScript" 1037 | } 1038 | } 1039 | ] 1040 | } 1041 | } 1042 | }, 1043 | { 1044 | "node": { 1045 | "id": "MDEwOlJlcG9zaXRvcnkyNDMwMTk5Nw==", 1046 | "name": "react-example", 1047 | "description": "Simple React example app", 1048 | "url": "https://github.com/mzabriskie/react-example", 1049 | "pushedAt": "2018-10-02T10:15:33Z", 1050 | "stargazers": { 1051 | "totalCount": 153 1052 | }, 1053 | "forkCount": 160, 1054 | "languages": { 1055 | "edges": [ 1056 | { 1057 | "node": { 1058 | "name": "CSS" 1059 | } 1060 | } 1061 | ] 1062 | } 1063 | } 1064 | }, 1065 | { 1066 | "node": { 1067 | "id": "MDEwOlJlcG9zaXRvcnk3ODQ5ODU2Mg==", 1068 | "name": "prettier-atom", 1069 | "description": "An atom package for the prettier formatter.", 1070 | "url": "https://github.com/prettier/prettier-atom", 1071 | "pushedAt": "2018-10-02T02:26:58Z", 1072 | "stargazers": { 1073 | "totalCount": 673 1074 | }, 1075 | "forkCount": 88, 1076 | "languages": { 1077 | "edges": [ 1078 | { 1079 | "node": { 1080 | "name": "JavaScript" 1081 | } 1082 | } 1083 | ] 1084 | } 1085 | } 1086 | }, 1087 | { 1088 | "node": { 1089 | "id": "MDEwOlJlcG9zaXRvcnkxMjk5OTcyMzY=", 1090 | "name": "learn-react", 1091 | "description": "Learn React with a laser focused, guided approach.", 1092 | "url": "https://github.com/kentcdodds/learn-react", 1093 | "pushedAt": "2018-09-30T20:21:53Z", 1094 | "stargazers": { 1095 | "totalCount": 107 1096 | }, 1097 | "forkCount": 34, 1098 | "languages": { 1099 | "edges": [ 1100 | { 1101 | "node": { 1102 | "name": "JavaScript" 1103 | } 1104 | } 1105 | ] 1106 | } 1107 | } 1108 | }, 1109 | { 1110 | "node": { 1111 | "id": "MDEwOlJlcG9zaXRvcnk3ODg5MzEwOA==", 1112 | "name": "prettier", 1113 | "description": "Prettier is an opinionated JavaScript formatter.", 1114 | "url": "https://github.com/kentcdodds/prettier", 1115 | "pushedAt": "2018-09-30T13:23:21Z", 1116 | "stargazers": { 1117 | "totalCount": 0 1118 | }, 1119 | "forkCount": 1, 1120 | "languages": { 1121 | "edges": [ 1122 | { 1123 | "node": { 1124 | "name": "JavaScript" 1125 | } 1126 | } 1127 | ] 1128 | } 1129 | } 1130 | }, 1131 | { 1132 | "node": { 1133 | "id": "MDEwOlJlcG9zaXRvcnkxMjUzNjgyMTg=", 1134 | "name": "reactjs.org", 1135 | "description": "The React documentation website", 1136 | "url": "https://github.com/kentcdodds/reactjs.org", 1137 | "pushedAt": "2018-09-28T14:55:47Z", 1138 | "stargazers": { 1139 | "totalCount": 0 1140 | }, 1141 | "forkCount": 1, 1142 | "languages": { 1143 | "edges": [ 1144 | { 1145 | "node": { 1146 | "name": "JavaScript" 1147 | } 1148 | } 1149 | ] 1150 | } 1151 | } 1152 | }, 1153 | { 1154 | "node": { 1155 | "id": "MDEwOlJlcG9zaXRvcnkxMzE5NDMzMjU=", 1156 | "name": "react-testing-library-course", 1157 | "description": "Test React with react-testing-library", 1158 | "url": "https://github.com/kentcdodds/react-testing-library-course", 1159 | "pushedAt": "2018-09-27T20:17:56Z", 1160 | "stargazers": { 1161 | "totalCount": 141 1162 | }, 1163 | "forkCount": 17, 1164 | "languages": { 1165 | "edges": [ 1166 | { 1167 | "node": { 1168 | "name": "JavaScript" 1169 | } 1170 | } 1171 | ] 1172 | } 1173 | } 1174 | }, 1175 | { 1176 | "node": { 1177 | "id": "MDEwOlJlcG9zaXRvcnkxNTAzMTk3MTg=", 1178 | "name": "react-suspense-simple-example", 1179 | "description": null, 1180 | "url": "https://github.com/kentcdodds/react-suspense-simple-example", 1181 | "pushedAt": "2018-09-25T22:30:45Z", 1182 | "stargazers": { 1183 | "totalCount": 36 1184 | }, 1185 | "forkCount": 0, 1186 | "languages": { 1187 | "edges": [ 1188 | { 1189 | "node": { 1190 | "name": "HTML" 1191 | } 1192 | } 1193 | ] 1194 | } 1195 | } 1196 | }, 1197 | { 1198 | "node": { 1199 | "id": "MDEwOlJlcG9zaXRvcnkxMjY0ODMxOTU=", 1200 | "name": "wait-for-expect", 1201 | "description": "Wait for expectation to be true, useful for integration and end to end testing. Integral part of react-testing-library.", 1202 | "url": "https://github.com/TheBrainFamily/wait-for-expect", 1203 | "pushedAt": "2018-09-25T13:54:10Z", 1204 | "stargazers": { 1205 | "totalCount": 72 1206 | }, 1207 | "forkCount": 4, 1208 | "languages": { 1209 | "edges": [ 1210 | { 1211 | "node": { 1212 | "name": "TypeScript" 1213 | } 1214 | } 1215 | ] 1216 | } 1217 | } 1218 | }, 1219 | { 1220 | "node": { 1221 | "id": "MDEwOlJlcG9zaXRvcnkxMjM0ODg4OA==", 1222 | "name": "genie", 1223 | "description": "Keyboard control for web applications (better than cryptic shortcuts). 3.5K minified & gzipped", 1224 | "url": "https://github.com/kentcdodds/genie", 1225 | "pushedAt": "2018-09-25T13:14:14Z", 1226 | "stargazers": { 1227 | "totalCount": 444 1228 | }, 1229 | "forkCount": 35, 1230 | "languages": { 1231 | "edges": [ 1232 | { 1233 | "node": { 1234 | "name": "JavaScript" 1235 | } 1236 | } 1237 | ] 1238 | } 1239 | } 1240 | }, 1241 | { 1242 | "node": { 1243 | "id": "MDEwOlJlcG9zaXRvcnkxNDk2NjkzMjA=", 1244 | "name": "docz", 1245 | "description": "✍🏻It has never been so easy to document your things!", 1246 | "url": "https://github.com/kentcdodds/docz", 1247 | "pushedAt": "2018-09-24T23:41:31Z", 1248 | "stargazers": { 1249 | "totalCount": 0 1250 | }, 1251 | "forkCount": 0, 1252 | "languages": { 1253 | "edges": [ 1254 | { 1255 | "node": { 1256 | "name": "TypeScript" 1257 | } 1258 | } 1259 | ] 1260 | } 1261 | } 1262 | }, 1263 | { 1264 | "node": { 1265 | "id": "MDEwOlJlcG9zaXRvcnkxMzkyNzk2NzA=", 1266 | "name": "jest-watch-select-projects", 1267 | "description": "Select which Jest projects to run", 1268 | "url": "https://github.com/kentcdodds/jest-watch-select-projects", 1269 | "pushedAt": "2018-09-20T17:58:20Z", 1270 | "stargazers": { 1271 | "totalCount": 0 1272 | }, 1273 | "forkCount": 0, 1274 | "languages": { 1275 | "edges": [ 1276 | { 1277 | "node": { 1278 | "name": "JavaScript" 1279 | } 1280 | } 1281 | ] 1282 | } 1283 | } 1284 | }, 1285 | { 1286 | "node": { 1287 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3OTIzMDc=", 1288 | "name": "confident-react-examples", 1289 | "description": null, 1290 | "url": "https://github.com/kentcdodds/confident-react-examples", 1291 | "pushedAt": "2018-09-20T16:58:21Z", 1292 | "stargazers": { 1293 | "totalCount": 10 1294 | }, 1295 | "forkCount": 0, 1296 | "languages": { 1297 | "edges": [ 1298 | { 1299 | "node": { 1300 | "name": "JavaScript" 1301 | } 1302 | } 1303 | ] 1304 | } 1305 | } 1306 | }, 1307 | { 1308 | "node": { 1309 | "id": "MDEwOlJlcG9zaXRvcnk0MTQ4NDg5OQ==", 1310 | "name": "eslint-config-kentcdodds", 1311 | "description": "ESLint configuration for projects that I do... Feel free to use this!", 1312 | "url": "https://github.com/kentcdodds/eslint-config-kentcdodds", 1313 | "pushedAt": "2018-09-20T16:07:09Z", 1314 | "stargazers": { 1315 | "totalCount": 70 1316 | }, 1317 | "forkCount": 20, 1318 | "languages": { 1319 | "edges": [ 1320 | { 1321 | "node": { 1322 | "name": "JavaScript" 1323 | } 1324 | } 1325 | ] 1326 | } 1327 | } 1328 | }, 1329 | { 1330 | "node": { 1331 | "id": "MDEwOlJlcG9zaXRvcnk5NzI3NjgzOQ==", 1332 | "name": "babel-plugin-macros-example", 1333 | "description": "An example of how you might make a babel-plugin-macros macro repo", 1334 | "url": "https://github.com/kentcdodds/babel-plugin-macros-example", 1335 | "pushedAt": "2018-09-16T22:39:39Z", 1336 | "stargazers": { 1337 | "totalCount": 18 1338 | }, 1339 | "forkCount": 6, 1340 | "languages": { 1341 | "edges": [ 1342 | { 1343 | "node": { 1344 | "name": "JavaScript" 1345 | } 1346 | } 1347 | ] 1348 | } 1349 | } 1350 | }, 1351 | { 1352 | "node": { 1353 | "id": "MDEwOlJlcG9zaXRvcnkxMzYxMDc5Mjc=", 1354 | "name": "downshift-examples", 1355 | "description": "Created with CodeSandbox", 1356 | "url": "https://github.com/kentcdodds/downshift-examples", 1357 | "pushedAt": "2018-09-14T13:43:09Z", 1358 | "stargazers": { 1359 | "totalCount": 33 1360 | }, 1361 | "forkCount": 8, 1362 | "languages": { 1363 | "edges": [ 1364 | { 1365 | "node": { 1366 | "name": "HTML" 1367 | } 1368 | } 1369 | ] 1370 | } 1371 | } 1372 | }, 1373 | { 1374 | "node": { 1375 | "id": "MDEwOlJlcG9zaXRvcnkxMDc1OTg0MDE=", 1376 | "name": "the-beginner-s-guide-to-reactjs", 1377 | "description": "The beginner's Guide to ReactJS course material for Egghead.io", 1378 | "url": "https://github.com/eggheadio-projects/the-beginner-s-guide-to-reactjs", 1379 | "pushedAt": "2018-09-12T10:00:23Z", 1380 | "stargazers": { 1381 | "totalCount": 135 1382 | }, 1383 | "forkCount": 64, 1384 | "languages": { 1385 | "edges": [ 1386 | { 1387 | "node": { 1388 | "name": "HTML" 1389 | } 1390 | } 1391 | ] 1392 | } 1393 | } 1394 | }, 1395 | { 1396 | "node": { 1397 | "id": "MDEwOlJlcG9zaXRvcnk5NjI4Mjc1MQ==", 1398 | "name": "babel-plugin-preval", 1399 | "description": "🐣 Pre-evaluate code at build-time", 1400 | "url": "https://github.com/kentcdodds/babel-plugin-preval", 1401 | "pushedAt": "2018-09-12T08:56:13Z", 1402 | "stargazers": { 1403 | "totalCount": 781 1404 | }, 1405 | "forkCount": 50, 1406 | "languages": { 1407 | "edges": [ 1408 | { 1409 | "node": { 1410 | "name": "JavaScript" 1411 | } 1412 | } 1413 | ] 1414 | } 1415 | } 1416 | }, 1417 | { 1418 | "node": { 1419 | "id": "MDEwOlJlcG9zaXRvcnkzODMyMTk4NQ==", 1420 | "name": "cloc", 1421 | "description": "An npm module for distributing cloc by Al Danial", 1422 | "url": "https://github.com/kentcdodds/cloc", 1423 | "pushedAt": "2018-09-11T01:02:43Z", 1424 | "stargazers": { 1425 | "totalCount": 80 1426 | }, 1427 | "forkCount": 8, 1428 | "languages": { 1429 | "edges": [ 1430 | { 1431 | "node": { 1432 | "name": "Perl" 1433 | } 1434 | } 1435 | ] 1436 | } 1437 | } 1438 | }, 1439 | { 1440 | "node": { 1441 | "id": "MDEwOlJlcG9zaXRvcnkxMTQzMTg5MTQ=", 1442 | "name": "typing-for-kids", 1443 | "description": "A little app I made for my kids for Christmas :)", 1444 | "url": "https://github.com/kentcdodds/typing-for-kids", 1445 | "pushedAt": "2018-09-08T16:44:41Z", 1446 | "stargazers": { 1447 | "totalCount": 26 1448 | }, 1449 | "forkCount": 5, 1450 | "languages": { 1451 | "edges": [ 1452 | { 1453 | "node": { 1454 | "name": "HTML" 1455 | } 1456 | } 1457 | ] 1458 | } 1459 | } 1460 | }, 1461 | { 1462 | "node": { 1463 | "id": "MDEwOlJlcG9zaXRvcnkzNDI5NTc1NA==", 1464 | "name": "es6-workshop", 1465 | "description": "A very hands on :open_hands: workshop :computer: about ES6 and beyond.", 1466 | "url": "https://github.com/kentcdodds/es6-workshop", 1467 | "pushedAt": "2018-09-05T15:04:05Z", 1468 | "stargazers": { 1469 | "totalCount": 256 1470 | }, 1471 | "forkCount": 78, 1472 | "languages": { 1473 | "edges": [ 1474 | { 1475 | "node": { 1476 | "name": "JavaScript" 1477 | } 1478 | } 1479 | ] 1480 | } 1481 | } 1482 | }, 1483 | { 1484 | "node": { 1485 | "id": "MDEwOlJlcG9zaXRvcnkxNDcwNDgyODk=", 1486 | "name": "jest-axe", 1487 | "description": "Custom Jest matcher for aXe for testing accessibility ♿️🃏", 1488 | "url": "https://github.com/kentcdodds/jest-axe", 1489 | "pushedAt": "2018-09-04T16:53:27Z", 1490 | "stargazers": { 1491 | "totalCount": 1 1492 | }, 1493 | "forkCount": 0, 1494 | "languages": { 1495 | "edges": [ 1496 | { 1497 | "node": { 1498 | "name": "JavaScript" 1499 | } 1500 | } 1501 | ] 1502 | } 1503 | } 1504 | }, 1505 | { 1506 | "node": { 1507 | "id": "MDEwOlJlcG9zaXRvcnkxNDY5MTkwMDg=", 1508 | "name": "babel-runtime-example", 1509 | "description": "An example of how to use @babel/plugin-transform-runtime", 1510 | "url": "https://github.com/kentcdodds/babel-runtime-example", 1511 | "pushedAt": "2018-08-31T16:48:42Z", 1512 | "stargazers": { 1513 | "totalCount": 15 1514 | }, 1515 | "forkCount": 1, 1516 | "languages": { 1517 | "edges": [ 1518 | { 1519 | "node": { 1520 | "name": "JavaScript" 1521 | } 1522 | } 1523 | ] 1524 | } 1525 | } 1526 | }, 1527 | { 1528 | "node": { 1529 | "id": "MDEwOlJlcG9zaXRvcnk2NjUyMzQ0MA==", 1530 | "name": "match-sorter", 1531 | "description": "Simple, expected, and deterministic best-match sorting of an array in JavaScript", 1532 | "url": "https://github.com/kentcdodds/match-sorter", 1533 | "pushedAt": "2018-08-29T23:44:21Z", 1534 | "stargazers": { 1535 | "totalCount": 1019 1536 | }, 1537 | "forkCount": 44, 1538 | "languages": { 1539 | "edges": [ 1540 | { 1541 | "node": { 1542 | "name": "JavaScript" 1543 | } 1544 | } 1545 | ] 1546 | } 1547 | } 1548 | }, 1549 | { 1550 | "node": { 1551 | "id": "MDEwOlJlcG9zaXRvcnk1NTQ3MDcxMw==", 1552 | "name": "refined-github", 1553 | "description": "Chrome extension that simplifies the GitHub interface and adds useful features", 1554 | "url": "https://github.com/kentcdodds/refined-github", 1555 | "pushedAt": "2018-08-28T19:08:48Z", 1556 | "stargazers": { 1557 | "totalCount": 1 1558 | }, 1559 | "forkCount": 0, 1560 | "languages": { 1561 | "edges": [ 1562 | { 1563 | "node": { 1564 | "name": "CSS" 1565 | } 1566 | } 1567 | ] 1568 | } 1569 | } 1570 | }, 1571 | { 1572 | "node": { 1573 | "id": "MDEwOlJlcG9zaXRvcnk2MzYzNTgwMg==", 1574 | "name": "webpack-config-utils", 1575 | "description": "Utilities to help your webpack config be easier to read", 1576 | "url": "https://github.com/kentcdodds/webpack-config-utils", 1577 | "pushedAt": "2018-08-28T17:12:45Z", 1578 | "stargazers": { 1579 | "totalCount": 212 1580 | }, 1581 | "forkCount": 19, 1582 | "languages": { 1583 | "edges": [ 1584 | { 1585 | "node": { 1586 | "name": "JavaScript" 1587 | } 1588 | } 1589 | ] 1590 | } 1591 | } 1592 | }, 1593 | { 1594 | "node": { 1595 | "id": "MDEwOlJlcG9zaXRvcnkyNTkyNzYxOQ==", 1596 | "name": "dotfiles", 1597 | "description": null, 1598 | "url": "https://github.com/kentcdodds/dotfiles", 1599 | "pushedAt": "2018-08-23T22:03:09Z", 1600 | "stargazers": { 1601 | "totalCount": 55 1602 | }, 1603 | "forkCount": 13, 1604 | "languages": { 1605 | "edges": [ 1606 | { 1607 | "node": { 1608 | "name": "Shell" 1609 | } 1610 | } 1611 | ] 1612 | } 1613 | } 1614 | }, 1615 | { 1616 | "node": { 1617 | "id": "MDEwOlJlcG9zaXRvcnkxMzYwODEzMTk=", 1618 | "name": "react-testing-library-examples", 1619 | "description": "Created with CodeSandbox", 1620 | "url": "https://github.com/kentcdodds/react-testing-library-examples", 1621 | "pushedAt": "2018-08-23T03:55:52Z", 1622 | "stargazers": { 1623 | "totalCount": 30 1624 | }, 1625 | "forkCount": 2, 1626 | "languages": { 1627 | "edges": [ 1628 | { 1629 | "node": { 1630 | "name": "HTML" 1631 | } 1632 | } 1633 | ] 1634 | } 1635 | } 1636 | }, 1637 | { 1638 | "node": { 1639 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3NzM0OTQ=", 1640 | "name": "rtl-manual-mock-ex", 1641 | "description": null, 1642 | "url": "https://github.com/kentcdodds/rtl-manual-mock-ex", 1643 | "pushedAt": "2018-08-22T23:26:34Z", 1644 | "stargazers": { 1645 | "totalCount": 0 1646 | }, 1647 | "forkCount": 0, 1648 | "languages": { 1649 | "edges": [ 1650 | { 1651 | "node": { 1652 | "name": "JavaScript" 1653 | } 1654 | } 1655 | ] 1656 | } 1657 | } 1658 | }, 1659 | { 1660 | "node": { 1661 | "id": "MDEwOlJlcG9zaXRvcnkxNDU3MzMwMTc=", 1662 | "name": "node-travis-ci", 1663 | "description": "node library to access the Travis-CI API", 1664 | "url": "https://github.com/kentcdodds/node-travis-ci", 1665 | "pushedAt": "2018-08-22T16:11:03Z", 1666 | "stargazers": { 1667 | "totalCount": 0 1668 | }, 1669 | "forkCount": 0, 1670 | "languages": { 1671 | "edges": [ 1672 | { 1673 | "node": { 1674 | "name": "JavaScript" 1675 | } 1676 | } 1677 | ] 1678 | } 1679 | } 1680 | }, 1681 | { 1682 | "node": { 1683 | "id": "MDEwOlJlcG9zaXRvcnkxNDU0NDg0NjE=", 1684 | "name": "from-html", 1685 | "description": "Get element references directly from a HTML string", 1686 | "url": "https://github.com/kentcdodds/from-html", 1687 | "pushedAt": "2018-08-20T17:20:14Z", 1688 | "stargazers": { 1689 | "totalCount": 0 1690 | }, 1691 | "forkCount": 0, 1692 | "languages": { 1693 | "edges": [ 1694 | { 1695 | "node": { 1696 | "name": "JavaScript" 1697 | } 1698 | } 1699 | ] 1700 | } 1701 | } 1702 | }, 1703 | { 1704 | "node": { 1705 | "id": "MDEwOlJlcG9zaXRvcnk4MzY0ODk1NQ==", 1706 | "name": "nps-utils", 1707 | "description": "Utilities for http://npm.im/nps (npm-package-scripts)", 1708 | "url": "https://github.com/kentcdodds/nps-utils", 1709 | "pushedAt": "2018-08-12T17:31:57Z", 1710 | "stargazers": { 1711 | "totalCount": 59 1712 | }, 1713 | "forkCount": 9, 1714 | "languages": { 1715 | "edges": [ 1716 | { 1717 | "node": { 1718 | "name": "JavaScript" 1719 | } 1720 | } 1721 | ] 1722 | } 1723 | } 1724 | }, 1725 | { 1726 | "node": { 1727 | "id": "MDEwOlJlcG9zaXRvcnkxMDAyOTgxMTk=", 1728 | "name": "codegen.macro", 1729 | "description": null, 1730 | "url": "https://github.com/kentcdodds/codegen.macro", 1731 | "pushedAt": "2018-08-08T16:32:38Z", 1732 | "stargazers": { 1733 | "totalCount": 9 1734 | }, 1735 | "forkCount": 2, 1736 | "languages": { 1737 | "edges": [ 1738 | { 1739 | "node": { 1740 | "name": "JavaScript" 1741 | } 1742 | } 1743 | ] 1744 | } 1745 | } 1746 | }, 1747 | { 1748 | "node": { 1749 | "id": "MDEwOlJlcG9zaXRvcnkxNDM3NDk2NTc=", 1750 | "name": "mdx", 1751 | "description": "JSX in Markdown for ambitious projects", 1752 | "url": "https://github.com/kentcdodds/mdx", 1753 | "pushedAt": "2018-08-06T15:53:02Z", 1754 | "stargazers": { 1755 | "totalCount": 0 1756 | }, 1757 | "forkCount": 0, 1758 | "languages": { 1759 | "edges": [ 1760 | { 1761 | "node": { 1762 | "name": "JavaScript" 1763 | } 1764 | } 1765 | ] 1766 | } 1767 | } 1768 | }, 1769 | { 1770 | "node": { 1771 | "id": "MDEwOlJlcG9zaXRvcnkxMTE4MzQzNDk=", 1772 | "name": "kit", 1773 | "description": "Tools for developing, documenting, and testing React component libraries", 1774 | "url": "https://github.com/c8r/kit", 1775 | "pushedAt": "2018-08-04T23:25:21Z", 1776 | "stargazers": { 1777 | "totalCount": 1052 1778 | }, 1779 | "forkCount": 28, 1780 | "languages": { 1781 | "edges": [ 1782 | { 1783 | "node": { 1784 | "name": "JavaScript" 1785 | } 1786 | } 1787 | ] 1788 | } 1789 | } 1790 | }, 1791 | { 1792 | "node": { 1793 | "id": "MDEwOlJlcG9zaXRvcnkxNDIyMjEyNzQ=", 1794 | "name": "graphql-request-1.7.0-bug", 1795 | "description": null, 1796 | "url": "https://github.com/kentcdodds/graphql-request-1.7.0-bug", 1797 | "pushedAt": "2018-08-03T17:36:18Z", 1798 | "stargazers": { 1799 | "totalCount": 0 1800 | }, 1801 | "forkCount": 0, 1802 | "languages": { 1803 | "edges": [ 1804 | { 1805 | "node": { 1806 | "name": "HTML" 1807 | } 1808 | } 1809 | ] 1810 | } 1811 | } 1812 | }, 1813 | { 1814 | "node": { 1815 | "id": "MDEwOlJlcG9zaXRvcnkxNDI1OTg4NTE=", 1816 | "name": "codesandbox-client", 1817 | "description": "An online code editor tailored for web application development 🏖️", 1818 | "url": "https://github.com/kentcdodds/codesandbox-client", 1819 | "pushedAt": "2018-07-27T17:45:54Z", 1820 | "stargazers": { 1821 | "totalCount": 0 1822 | }, 1823 | "forkCount": 0, 1824 | "languages": { 1825 | "edges": [ 1826 | { 1827 | "node": { 1828 | "name": "Dockerfile" 1829 | } 1830 | } 1831 | ] 1832 | } 1833 | } 1834 | }, 1835 | { 1836 | "node": { 1837 | "id": "MDEwOlJlcG9zaXRvcnkxMDI4NzYwMjM=", 1838 | "name": "emotion", 1839 | "description": "⚡️ The Next Generation of CSS-in-JS", 1840 | "url": "https://github.com/kentcdodds/emotion", 1841 | "pushedAt": "2018-07-26T05:36:34Z", 1842 | "stargazers": { 1843 | "totalCount": 1 1844 | }, 1845 | "forkCount": 0, 1846 | "languages": { 1847 | "edges": [ 1848 | { 1849 | "node": { 1850 | "name": "JavaScript" 1851 | } 1852 | } 1853 | ] 1854 | } 1855 | } 1856 | }, 1857 | { 1858 | "node": { 1859 | "id": "MDEwOlJlcG9zaXRvcnkxNDE2MzUwMjY=", 1860 | "name": "webpack-serve", 1861 | "description": "A lean, modern, and flexible webpack development server", 1862 | "url": "https://github.com/kentcdodds/webpack-serve", 1863 | "pushedAt": "2018-07-19T22:02:13Z", 1864 | "stargazers": { 1865 | "totalCount": 0 1866 | }, 1867 | "forkCount": 0, 1868 | "languages": { 1869 | "edges": [ 1870 | { 1871 | "node": { 1872 | "name": "JavaScript" 1873 | } 1874 | } 1875 | ] 1876 | } 1877 | } 1878 | }, 1879 | { 1880 | "node": { 1881 | "id": "MDEwOlJlcG9zaXRvcnkxNDAzMTA4ODU=", 1882 | "name": "shallow-to-mock", 1883 | "description": null, 1884 | "url": "https://github.com/kentcdodds/shallow-to-mock", 1885 | "pushedAt": "2018-07-09T16:14:19Z", 1886 | "stargazers": { 1887 | "totalCount": 13 1888 | }, 1889 | "forkCount": 0, 1890 | "languages": { 1891 | "edges": [ 1892 | { 1893 | "node": { 1894 | "name": "JavaScript" 1895 | } 1896 | } 1897 | ] 1898 | } 1899 | } 1900 | }, 1901 | { 1902 | "node": { 1903 | "id": "MDEwOlJlcG9zaXRvcnk4NjEwMDIwNg==", 1904 | "name": "jest-glamor-react", 1905 | "description": "Jest utilities for Glamor and React", 1906 | "url": "https://github.com/kentcdodds/jest-glamor-react", 1907 | "pushedAt": "2018-07-03T20:37:01Z", 1908 | "stargazers": { 1909 | "totalCount": 85 1910 | }, 1911 | "forkCount": 22, 1912 | "languages": { 1913 | "edges": [ 1914 | { 1915 | "node": { 1916 | "name": "JavaScript" 1917 | } 1918 | } 1919 | ] 1920 | } 1921 | } 1922 | }, 1923 | { 1924 | "node": { 1925 | "id": "MDEwOlJlcG9zaXRvcnk4MDY0NDI4Ng==", 1926 | "name": "eslint-plugin-react", 1927 | "description": "React specific linting rules for ESLint", 1928 | "url": "https://github.com/kentcdodds/eslint-plugin-react", 1929 | "pushedAt": "2018-07-03T00:48:35Z", 1930 | "stargazers": { 1931 | "totalCount": 0 1932 | }, 1933 | "forkCount": 0, 1934 | "languages": { 1935 | "edges": [ 1936 | { 1937 | "node": { 1938 | "name": "JavaScript" 1939 | } 1940 | } 1941 | ] 1942 | } 1943 | } 1944 | }, 1945 | { 1946 | "node": { 1947 | "id": "MDEwOlJlcG9zaXRvcnkzODU4MzM4NA==", 1948 | "name": "ama", 1949 | "description": "Ask me anything!", 1950 | "url": "https://github.com/kentcdodds/ama", 1951 | "pushedAt": "2018-07-02T00:22:46Z", 1952 | "stargazers": { 1953 | "totalCount": 347 1954 | }, 1955 | "forkCount": 33, 1956 | "languages": { 1957 | "edges": [] 1958 | } 1959 | } 1960 | }, 1961 | { 1962 | "node": { 1963 | "id": "MDEwOlJlcG9zaXRvcnk5NzI1MjA1MA==", 1964 | "name": "preval.macro", 1965 | "description": "Pre-evaluate code at build-time with babel-macros", 1966 | "url": "https://github.com/kentcdodds/preval.macro", 1967 | "pushedAt": "2018-06-27T22:10:41Z", 1968 | "stargazers": { 1969 | "totalCount": 29 1970 | }, 1971 | "forkCount": 2, 1972 | "languages": { 1973 | "edges": [ 1974 | { 1975 | "node": { 1976 | "name": "JavaScript" 1977 | } 1978 | } 1979 | ] 1980 | } 1981 | } 1982 | }, 1983 | { 1984 | "node": { 1985 | "id": "MDEwOlJlcG9zaXRvcnkxMzg5MzY4MzQ=", 1986 | "name": "npm-1", 1987 | "description": ":ship: Set of semantic-release plugins to publish to a npm registry", 1988 | "url": "https://github.com/kentcdodds/npm-1", 1989 | "pushedAt": "2018-06-27T22:06:37Z", 1990 | "stargazers": { 1991 | "totalCount": 0 1992 | }, 1993 | "forkCount": 0, 1994 | "languages": { 1995 | "edges": [ 1996 | { 1997 | "node": { 1998 | "name": "JavaScript" 1999 | } 2000 | } 2001 | ] 2002 | } 2003 | } 2004 | }, 2005 | { 2006 | "node": { 2007 | "id": "MDEwOlJlcG9zaXRvcnkxMDAxNjAwMTQ=", 2008 | "name": "babel-plugin-codegen", 2009 | "description": "💥 Generate code at build-time", 2010 | "url": "https://github.com/kentcdodds/babel-plugin-codegen", 2011 | "pushedAt": "2018-06-27T21:21:28Z", 2012 | "stargazers": { 2013 | "totalCount": 150 2014 | }, 2015 | "forkCount": 12, 2016 | "languages": { 2017 | "edges": [ 2018 | { 2019 | "node": { 2020 | "name": "JavaScript" 2021 | } 2022 | } 2023 | ] 2024 | } 2025 | } 2026 | }, 2027 | { 2028 | "node": { 2029 | "id": "MDEwOlJlcG9zaXRvcnk0MDI0MjA1MQ==", 2030 | "name": "starwars-names", 2031 | "description": "Get a random Star Wars name", 2032 | "url": "https://github.com/kentcdodds/starwars-names", 2033 | "pushedAt": "2018-06-26T23:08:03Z", 2034 | "stargazers": { 2035 | "totalCount": 132 2036 | }, 2037 | "forkCount": 139, 2038 | "languages": { 2039 | "edges": [ 2040 | { 2041 | "node": { 2042 | "name": "JavaScript" 2043 | } 2044 | } 2045 | ] 2046 | } 2047 | } 2048 | }, 2049 | { 2050 | "node": { 2051 | "id": "MDEwOlJlcG9zaXRvcnkxMzg3NzE2NzI=", 2052 | "name": "create-react-app-react-testing-library-example", 2053 | "description": null, 2054 | "url": "https://github.com/kentcdodds/create-react-app-react-testing-library-example", 2055 | "pushedAt": "2018-06-26T17:35:07Z", 2056 | "stargazers": { 2057 | "totalCount": 11 2058 | }, 2059 | "forkCount": 1, 2060 | "languages": { 2061 | "edges": [ 2062 | { 2063 | "node": { 2064 | "name": "HTML" 2065 | } 2066 | } 2067 | ] 2068 | } 2069 | } 2070 | }, 2071 | { 2072 | "node": { 2073 | "id": "MDEwOlJlcG9zaXRvcnk4NDI2MDQ0MQ==", 2074 | "name": "create-react-app", 2075 | "description": "Create React apps with no build configuration.", 2076 | "url": "https://github.com/kentcdodds/create-react-app", 2077 | "pushedAt": "2018-06-26T16:46:12Z", 2078 | "stargazers": { 2079 | "totalCount": 0 2080 | }, 2081 | "forkCount": 1, 2082 | "languages": { 2083 | "edges": [ 2084 | { 2085 | "node": { 2086 | "name": "JavaScript" 2087 | } 2088 | } 2089 | ] 2090 | } 2091 | } 2092 | }, 2093 | { 2094 | "node": { 2095 | "id": "MDEwOlJlcG9zaXRvcnk1NDEzNjMwNg==", 2096 | "name": "eslint-find-rules", 2097 | "description": "Find built-in ESLint rules you don't have in your custom config", 2098 | "url": "https://github.com/sarbbottam/eslint-find-rules", 2099 | "pushedAt": "2018-06-25T20:26:41Z", 2100 | "stargazers": { 2101 | "totalCount": 100 2102 | }, 2103 | "forkCount": 15, 2104 | "languages": { 2105 | "edges": [ 2106 | { 2107 | "node": { 2108 | "name": "JavaScript" 2109 | } 2110 | } 2111 | ] 2112 | } 2113 | } 2114 | }, 2115 | { 2116 | "node": { 2117 | "id": "MDEwOlJlcG9zaXRvcnkxMzg2MjkxMzU=", 2118 | "name": "never-use-shallow-rendering", 2119 | "description": null, 2120 | "url": "https://github.com/kentcdodds/never-use-shallow-rendering", 2121 | "pushedAt": "2018-06-25T17:38:42Z", 2122 | "stargazers": { 2123 | "totalCount": 14 2124 | }, 2125 | "forkCount": 0, 2126 | "languages": { 2127 | "edges": [ 2128 | { 2129 | "node": { 2130 | "name": "JavaScript" 2131 | } 2132 | } 2133 | ] 2134 | } 2135 | } 2136 | }, 2137 | { 2138 | "node": { 2139 | "id": "MDEwOlJlcG9zaXRvcnkxMzc5NzUwMTI=", 2140 | "name": "js-mocking-fundamentals", 2141 | "description": null, 2142 | "url": "https://github.com/kentcdodds/js-mocking-fundamentals", 2143 | "pushedAt": "2018-06-23T03:03:58Z", 2144 | "stargazers": { 2145 | "totalCount": 37 2146 | }, 2147 | "forkCount": 3, 2148 | "languages": { 2149 | "edges": [ 2150 | { 2151 | "node": { 2152 | "name": "JavaScript" 2153 | } 2154 | } 2155 | ] 2156 | } 2157 | } 2158 | }, 2159 | { 2160 | "node": { 2161 | "id": "MDEwOlJlcG9zaXRvcnkzMDQ0NjYzNg==", 2162 | "name": "api-check", 2163 | "description": "VanillaJS version of ReactJS propTypes", 2164 | "url": "https://github.com/kentcdodds/api-check", 2165 | "pushedAt": "2018-06-18T19:36:24Z", 2166 | "stargazers": { 2167 | "totalCount": 193 2168 | }, 2169 | "forkCount": 26, 2170 | "languages": { 2171 | "edges": [ 2172 | { 2173 | "node": { 2174 | "name": "HTML" 2175 | } 2176 | } 2177 | ] 2178 | } 2179 | } 2180 | }, 2181 | { 2182 | "node": { 2183 | "id": "MDEwOlJlcG9zaXRvcnkxMzc3ODcxNDQ=", 2184 | "name": "react-test-isolation", 2185 | "description": null, 2186 | "url": "https://github.com/kentcdodds/react-test-isolation", 2187 | "pushedAt": "2018-06-18T18:02:40Z", 2188 | "stargazers": { 2189 | "totalCount": 9 2190 | }, 2191 | "forkCount": 0, 2192 | "languages": { 2193 | "edges": [ 2194 | { 2195 | "node": { 2196 | "name": "HTML" 2197 | } 2198 | } 2199 | ] 2200 | } 2201 | } 2202 | } 2203 | ] 2204 | }, 2205 | "organizations": { 2206 | "edges": [ 2207 | { 2208 | "node": { 2209 | "avatarUrl": "https://avatars3.githubusercontent.com/u/139426?v=4", 2210 | "id": "MDEyOk9yZ2FuaXphdGlvbjEzOTQyNg==", 2211 | "login": "angular" 2212 | } 2213 | }, 2214 | { 2215 | "node": { 2216 | "avatarUrl": "https://avatars1.githubusercontent.com/u/476675?v=4", 2217 | "id": "MDEyOk9yZ2FuaXphdGlvbjQ3NjY3NQ==", 2218 | "login": "paypal" 2219 | } 2220 | }, 2221 | { 2222 | "node": { 2223 | "avatarUrl": "https://avatars2.githubusercontent.com/u/1725583?v=4", 2224 | "id": "MDEyOk9yZ2FuaXphdGlvbjE3MjU1ODM=", 2225 | "login": "tc39" 2226 | } 2227 | }, 2228 | { 2229 | "node": { 2230 | "avatarUrl": "https://avatars2.githubusercontent.com/u/2105791?v=4", 2231 | "id": "MDEyOk9yZ2FuaXphdGlvbjIxMDU3OTE=", 2232 | "login": "webpack" 2233 | } 2234 | }, 2235 | { 2236 | "node": { 2237 | "avatarUrl": "https://avatars2.githubusercontent.com/u/5658226?v=4", 2238 | "id": "MDEyOk9yZ2FuaXphdGlvbjU2NTgyMjY=", 2239 | "login": "expressjs" 2240 | } 2241 | }, 2242 | { 2243 | "node": { 2244 | "avatarUrl": "https://avatars3.githubusercontent.com/u/6627683?v=4", 2245 | "id": "MDEyOk9yZ2FuaXphdGlvbjY2Mjc2ODM=", 2246 | "login": "gdgutah" 2247 | } 2248 | }, 2249 | { 2250 | "node": { 2251 | "avatarUrl": "https://avatars3.githubusercontent.com/u/7704921?v=4", 2252 | "id": "MDEyOk9yZ2FuaXphdGlvbjc3MDQ5MjE=", 2253 | "login": "github-beta" 2254 | } 2255 | }, 2256 | { 2257 | "node": { 2258 | "avatarUrl": "https://avatars2.githubusercontent.com/u/8039323?v=4", 2259 | "id": "MDEyOk9yZ2FuaXphdGlvbjgwMzkzMjM=", 2260 | "login": "airpair" 2261 | } 2262 | }, 2263 | { 2264 | "node": { 2265 | "avatarUrl": "https://avatars2.githubusercontent.com/u/8120632?v=4", 2266 | "id": "MDEyOk9yZ2FuaXphdGlvbjgxMjA2MzI=", 2267 | "login": "SLC-JS-Learners" 2268 | } 2269 | }, 2270 | { 2271 | "node": { 2272 | "avatarUrl": "https://avatars3.githubusercontent.com/u/8561755?v=4", 2273 | "id": "MDEyOk9yZ2FuaXphdGlvbjg1NjE3NTU=", 2274 | "login": "formly-js" 2275 | } 2276 | }, 2277 | { 2278 | "node": { 2279 | "avatarUrl": "https://avatars1.githubusercontent.com/u/9486604?v=4", 2280 | "id": "MDEyOk9yZ2FuaXphdGlvbjk0ODY2MDQ=", 2281 | "login": "AngularAir" 2282 | } 2283 | }, 2284 | { 2285 | "node": { 2286 | "avatarUrl": "https://avatars1.githubusercontent.com/u/9637642?v=4", 2287 | "id": "MDEyOk9yZ2FuaXphdGlvbjk2Mzc2NDI=", 2288 | "login": "babel" 2289 | } 2290 | }, 2291 | { 2292 | "node": { 2293 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11040183?v=4", 2294 | "id": "MDEyOk9yZ2FuaXphdGlvbjExMDQwMTgz", 2295 | "login": "ReactWeek" 2296 | } 2297 | }, 2298 | { 2299 | "node": { 2300 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11279140?v=4", 2301 | "id": "MDEyOk9yZ2FuaXphdGlvbjExMjc5MTQw", 2302 | "login": "forms-js" 2303 | } 2304 | }, 2305 | { 2306 | "node": { 2307 | "avatarUrl": "https://avatars2.githubusercontent.com/u/11602678?v=4", 2308 | "id": "MDEyOk9yZ2FuaXphdGlvbjExNjAyNjc4", 2309 | "login": "AngularClass" 2310 | } 2311 | }, 2312 | { 2313 | "node": { 2314 | "avatarUrl": "https://avatars0.githubusercontent.com/u/11980392?v=4", 2315 | "id": "MDEyOk9yZ2FuaXphdGlvbjExOTgwMzky", 2316 | "login": "commitizen" 2317 | } 2318 | }, 2319 | { 2320 | "node": { 2321 | "avatarUrl": "https://avatars2.githubusercontent.com/u/13523395?v=4", 2322 | "id": "MDEyOk9yZ2FuaXphdGlvbjEzNTIzMzk1", 2323 | "login": "istanbuljs" 2324 | } 2325 | }, 2326 | { 2327 | "node": { 2328 | "avatarUrl": "https://avatars3.githubusercontent.com/u/14825423?v=4", 2329 | "id": "MDEyOk9yZ2FuaXphdGlvbjE0ODI1NDIz", 2330 | "login": "angular-todo-mvc" 2331 | } 2332 | }, 2333 | { 2334 | "node": { 2335 | "avatarUrl": "https://avatars2.githubusercontent.com/u/15644605?v=4", 2336 | "id": "MDEyOk9yZ2FuaXphdGlvbjE1NjQ0NjA1", 2337 | "login": "eggheadio-github" 2338 | } 2339 | }, 2340 | { 2341 | "node": { 2342 | "avatarUrl": "https://avatars3.githubusercontent.com/u/15834066?v=4", 2343 | "id": "MDEyOk9yZ2FuaXphdGlvbjE1ODM0MDY2", 2344 | "login": "javascriptair" 2345 | } 2346 | }, 2347 | { 2348 | "node": { 2349 | "avatarUrl": "https://avatars3.githubusercontent.com/u/16443515?v=4", 2350 | "id": "MDEyOk9yZ2FuaXphdGlvbjE2NDQzNTE1", 2351 | "login": "conventional-changelog" 2352 | } 2353 | }, 2354 | { 2355 | "node": { 2356 | "avatarUrl": "https://avatars2.githubusercontent.com/u/20658825?v=4", 2357 | "id": "MDEyOk9yZ2FuaXphdGlvbjIwNjU4ODI1", 2358 | "login": "styled-components" 2359 | } 2360 | }, 2361 | { 2362 | "node": { 2363 | "avatarUrl": "https://avatars0.githubusercontent.com/u/25333538?v=4", 2364 | "id": "MDEyOk9yZ2FuaXphdGlvbjI1MzMzNTM4", 2365 | "login": "maintainers" 2366 | } 2367 | }, 2368 | { 2369 | "node": { 2370 | "avatarUrl": "https://avatars2.githubusercontent.com/u/25822731?v=4", 2371 | "id": "MDEyOk9yZ2FuaXphdGlvbjI1ODIyNzMx", 2372 | "login": "prettier" 2373 | } 2374 | }, 2375 | { 2376 | "node": { 2377 | "avatarUrl": "https://avatars3.githubusercontent.com/u/28913597?v=4", 2378 | "id": "MDEyOk9yZ2FuaXphdGlvbjI4OTEzNTk3", 2379 | "login": "babel-utils" 2380 | } 2381 | } 2382 | ] 2383 | } 2384 | } 2385 | } 2386 | --------------------------------------------------------------------------------