diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..c95e241 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["es2015", "next/babel"], + "plugins": ["styled-components-require"] +} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..8fa06d7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +build +node_modules +coverage diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..9218929 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,50 @@ +{ + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "ecmaFeatures": { + "modules": true, + "jsx": true, + "experimentalObjectRestSpread": true + }, + "env": { + "amd": true, + "browser": true, + "es6": true, + "jquery": true, + "node": true + }, + "rules": { + "comma-dangle": [2, "never"], + "no-cond-assign": 2, + "no-debugger": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty": 2, + "no-empty-function": 2, + "no-func-assign": 2, + "no-sparse-arrays": 2, + "eqeqeq": 2, + "no-eval": 2, + "no-magic-numbers": 0, + "no-lone-blocks": 2, + "no-redeclare": 2, + "no-unused-expressions": 0, + "no-useless-concat": 2, + "no-unused-vars": 2, + "no-use-before-define": 2, + "no-lonely-if": 2, + "no-mixed-spaces-and-tabs": 2, + "new-cap": 2, + "indent": [2, 2], + "vars-on-top": 2, + "semi": [2, "never"], + "react/jsx-uses-vars": 2, + "react/jsx-uses-react": 2 + }, + "parserOptions": { + "sourceType": "module", + } +} diff --git a/.gitignore b/.gitignore index fd318ae..aad8cc4 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ build/Release # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git node_modules -npm-janitor/bundle.js +.next diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..afb3524 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - "7" +cache: + directories: + - node_modules +notifications: + email: false diff --git a/api/__mocks__/user.js b/api/__mocks__/user.js new file mode 100644 index 0000000..5dfdbf6 --- /dev/null +++ b/api/__mocks__/user.js @@ -0,0 +1,14 @@ +let modules = [{ + name: 'npm-janitor-web', + valid: true, + errors: [], + warnings: ['Add recommended field: contributors'], + suggestions: ['Add optional field: homepage'] +}] + +const get = username => new Promise(resolve => { + if (username) resolve({modules: modules}) + else resolve({modules: []}) +}) + +export default get diff --git a/api/user.js b/api/user.js new file mode 100644 index 0000000..1b7cfd1 --- /dev/null +++ b/api/user.js @@ -0,0 +1,20 @@ +import axios from 'axios' + +const get = (username) => { + if (!username) return {modules: []} + return axios.get(`https://npm-janitor-api.now.sh/${username}`) + .then(response => { + let modules = response.data + modules = modules.map(module => ({ + name: module.module, + valid: module.info.valid, + errors: module.info.errors || [], + warnings: module.info.warnings || [], + suggestions: module.info.recommendations || [] + })) + return {modules: modules} + }) + .catch(error => ({error: error.response.data.message})) +} + +export default get diff --git a/components/HotLink.jsx b/components/HotLink.jsx deleted file mode 100644 index 7e69f68..0000000 --- a/components/HotLink.jsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import Cards from './cards.jsx'; -import Loader from 'react-loader'; - -class HotLink extends React.Component { - constructor (props) { - super(props); - this.state = {json: [], loaded:false}; - } - _fetch (name) { - fetch(`https://npm-janitor.herokuapp.com/api/${name.toLowerCase()}`). - then((data) => data.json()). - then((json) => this.setState({json, loaded: true})); - } - render() { - this._fetch(this.props.params.user); - return - - - } -} - -HotLink.contextTypes = { - router: React.PropTypes.func.isRequired -}; - -export default HotLink; diff --git a/components/__snapshots__/card.test.js.snap b/components/__snapshots__/card.test.js.snap new file mode 100644 index 0000000..c12c620 --- /dev/null +++ b/components/__snapshots__/card.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/components/__snapshots__/code.test.js.snap b/components/__snapshots__/code.test.js.snap new file mode 100644 index 0000000..0a1acdb --- /dev/null +++ b/components/__snapshots__/code.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/components/__snapshots__/grid.test.js.snap b/components/__snapshots__/grid.test.js.snap new file mode 100644 index 0000000..df26f31 --- /dev/null +++ b/components/__snapshots__/grid.test.js.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+
+ 1 +
+
+ 2 +
+
+ 3 +
+
+`; diff --git a/components/__snapshots__/header.test.js.snap b/components/__snapshots__/header.test.js.snap new file mode 100644 index 0000000..6ceaa18 --- /dev/null +++ b/components/__snapshots__/header.test.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+
+ +
+ + + ↵ + +
+
+
+
+`; diff --git a/components/__snapshots__/heavy.test.js.snap b/components/__snapshots__/heavy.test.js.snap new file mode 100644 index 0000000..ddf1a81 --- /dev/null +++ b/components/__snapshots__/heavy.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+ This is some bold text +
+`; diff --git a/components/__snapshots__/hint.test.js.snap b/components/__snapshots__/hint.test.js.snap new file mode 100644 index 0000000..0db163f --- /dev/null +++ b/components/__snapshots__/hint.test.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/components/__snapshots__/input.test.js.snap b/components/__snapshots__/input.test.js.snap new file mode 100644 index 0000000..8b8a56e --- /dev/null +++ b/components/__snapshots__/input.test.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/components/__snapshots__/link.test.js.snap b/components/__snapshots__/link.test.js.snap new file mode 100644 index 0000000..2a8d239 --- /dev/null +++ b/components/__snapshots__/link.test.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` + +`; diff --git a/components/__snapshots__/loading.test.js.snap b/components/__snapshots__/loading.test.js.snap new file mode 100644 index 0000000..10bc94b --- /dev/null +++ b/components/__snapshots__/loading.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+`; diff --git a/components/__snapshots__/logo.test.js.snap b/components/__snapshots__/logo.test.js.snap new file mode 100644 index 0000000..e533ebf --- /dev/null +++ b/components/__snapshots__/logo.test.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders big version 1`] = ` + +`; + +exports[`renders small version 1`] = ` + +`; diff --git a/components/__snapshots__/module.test.js.snap b/components/__snapshots__/module.test.js.snap new file mode 100644 index 0000000..772d600 --- /dev/null +++ b/components/__snapshots__/module.test.js.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders invalid module 1`] = ` + + +
+ module +
+
+
+
+
+ + 1 + + error +
+
+ - + name is not allowed +
+
+
+ + +
+
+ +`; + +exports[`renders valid module 1`] = ` + + +
+ module +
+
+
+ + + +
+
+ +`; diff --git a/components/__snapshots__/score.test.js.snap b/components/__snapshots__/score.test.js.snap new file mode 100644 index 0000000..b60d0b7 --- /dev/null +++ b/components/__snapshots__/score.test.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly when module has errors 1`] = ` +
+`; + +exports[`renders correctly when score <= 6 1`] = ` +
+`; + +exports[`renders correctly when score > 6 1`] = ` +
+`; diff --git a/components/__snapshots__/tips.test.js.snap b/components/__snapshots__/tips.test.js.snap new file mode 100644 index 0000000..c25467f --- /dev/null +++ b/components/__snapshots__/tips.test.js.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+
+
+ + 1 + + error +
+
+ - + name is not valid +
+
+
+
+ Add recommended + field + : + + + contributors + +
+
+
+
+
+ Add optional + field + : + + + engines + +
+
+
+
+
+`; diff --git a/components/card.js b/components/card.js new file mode 100644 index 0000000..fbbdeb1 --- /dev/null +++ b/components/card.js @@ -0,0 +1,10 @@ +const Card = styled.span` + background: #FFF; + margin: 10px; + padding: 20px; + display: inline-block; + border-radius: 2px; + box-shadow: 0px 1px 2px #DDD; +` + +export default Card diff --git a/components/card.test.js b/components/card.test.js new file mode 100644 index 0000000..41e12b5 --- /dev/null +++ b/components/card.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Card from './card' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/cards.jsx b/components/cards.jsx deleted file mode 100644 index fc36b36..0000000 --- a/components/cards.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import Card from 'react-material-card'; - -export default class Cards extends React.Component { - constructor (props) { - super(props); - } - - render () { - return
- {this.props.modules.map((d) =>{ - d.info.warnings = d.info.warnings || [] - d.info.errors = d.info.errors || [] - d.info.recommendations = d.info.recommendations || [] - return -

- {d.module} -

- { d.info.warnings.map( (v) =>

{v}

)} - { d.info.errors.map( (v) =>

{v}

)} - { d.info.recommendations.map( (v) =>

{v}

)} -
- })} -
- } -} diff --git a/components/code.js b/components/code.js new file mode 100644 index 0000000..fa04b37 --- /dev/null +++ b/components/code.js @@ -0,0 +1,7 @@ +const Code = styled.span` + font-family: monospace; + font-size: 15px; + color: #555; +` + +export default Code diff --git a/components/code.test.js b/components/code.test.js new file mode 100644 index 0000000..230e0d7 --- /dev/null +++ b/components/code.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Code from './code' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/grid.js b/components/grid.js new file mode 100644 index 0000000..3e06002 --- /dev/null +++ b/components/grid.js @@ -0,0 +1,7 @@ +const Grid = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: space-around; +` + +export default Grid diff --git a/components/grid.test.js b/components/grid.test.js new file mode 100644 index 0000000..428f1cf --- /dev/null +++ b/components/grid.test.js @@ -0,0 +1,14 @@ +import React from 'react' +import Grid from './grid' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + +
1
+
2
+
3
+
+ ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/header.js b/components/header.js new file mode 100644 index 0000000..b5aa3c6 --- /dev/null +++ b/components/header.js @@ -0,0 +1,67 @@ +import Router from 'next/router' +import Logo from './logo' +import Input from './input' +import Loading from './loading' +import Hint from './hint' + +const timingFunction = 'cubic-bezier(0.19, 1, 0.22, 1)' + +const Wrapper = styled.div` + height: ${props => props.full ? '100vh' : '200px'}; + background: #3EA6D6; + text-align: center; + display: flex; + align-items: center; + justify-content: center; + transition: height 1s ${timingFunction}; +` + +const Header = class extends React.Component { + constructor (props) { + super(props) + this.state = { + username: props.username || '', + full: props.full, + loading: false + } + } + + componentWillReceiveProps (props) { + this.setState({full: props.full, loading: props.loading}) + } + + onChange = event => this.setState({username: event.target.value}) + + onKeyUp = event => { + if (event.which === 13 && this.state.username) { + Router.push(`/user?name=${this.state.username}`) + /* + The user page is rendered on the server, + which takes a little time, we can animate + the header to give a perception of better transition + */ + this.setState({loading: true, full: true}) + } + } + + render () { + return +
+ +
+ + + +
+
+
+ } +} + +export default Header diff --git a/components/header.test.js b/components/header.test.js new file mode 100644 index 0000000..99ce90f --- /dev/null +++ b/components/header.test.js @@ -0,0 +1,19 @@ +import React from 'react' +import Header from './header' +import renderer from 'react-test-renderer' + +jest.mock('next/router') + +it('renders correctly', () => { + let props = {full: false} + const component = renderer.create(
) + let tree = component.toJSON() + + let input = tree.children[0].children[1].children[0] + input.props.onChange({target: {value: 'siddharthkp'}}) + input.props.onKeyUp({which: 65}) + input.props.onKeyUp({which: 13}) + + tree = component.toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/heavy.js b/components/heavy.js new file mode 100644 index 0000000..76f7a2f --- /dev/null +++ b/components/heavy.js @@ -0,0 +1,7 @@ +const Heavy = styled.div` + font-weight: 700; + margin-bottom: 10px; + color: ${props => props.color ? props.color : 'inherit'}; +` + +export default Heavy diff --git a/components/heavy.test.js b/components/heavy.test.js new file mode 100644 index 0000000..01a0ccd --- /dev/null +++ b/components/heavy.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Heavy from './heavy' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + This is some bold text + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/hint.js b/components/hint.js new file mode 100644 index 0000000..6702571 --- /dev/null +++ b/components/hint.js @@ -0,0 +1,19 @@ +import {keyframes} from 'styled-components' + +const blink = keyframes` + 0% {opacity: 0} + 50% {opacity: 1} + 100% {opacity: 0} +` + +const Hint = styled.span` + margin: 20px auto 0; + height: 25px; + color: #777; + position: relative; + left: -40px; + opacity: ${props => props.show ? 1 : 0}; + animation: ${props => props.blink ? blink : 'none'} 2s linear infinite; +` + +export default Hint diff --git a/components/hint.test.js b/components/hint.test.js new file mode 100644 index 0000000..0e13405 --- /dev/null +++ b/components/hint.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Hint from './hint' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/images/minion.gif b/components/images/minion.gif deleted file mode 100644 index 0ea9466..0000000 Binary files a/components/images/minion.gif and /dev/null differ diff --git a/components/input.js b/components/input.js new file mode 100644 index 0000000..9c48c0c --- /dev/null +++ b/components/input.js @@ -0,0 +1,19 @@ +const timingFunction = 'cubic-bezier(0.19, 1, 0.22, 1)' + +const Input = styled.input` + border: none; + font-size: 18px; + padding: 20px; + width: ${props => props.full ? '80vw' : '30vw'} + margin: 10px; + margin-top: -3px; + outline: none; + color: #333; + font-family: 'Lato', sans-serif; + transition: width 1s ${timingFunction}; + @media (max-width: 720px) { + width: 80vw; + } +` + +export default Input diff --git a/components/input.test.js b/components/input.test.js new file mode 100644 index 0000000..564911e --- /dev/null +++ b/components/input.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Input from './input' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/legend.jsx b/components/legend.jsx deleted file mode 100644 index bc758a4..0000000 --- a/components/legend.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -export default class Legend extends React.Component { - constructor (props) { - super(props); - } - - render () { - - } -} diff --git a/components/link.js b/components/link.js new file mode 100644 index 0000000..58dc485 --- /dev/null +++ b/components/link.js @@ -0,0 +1,6 @@ +const Link = styled.a` + color: #3EA6D6; + text-decoration: none; +` + +export default Link diff --git a/components/link.test.js b/components/link.test.js new file mode 100644 index 0000000..131d313 --- /dev/null +++ b/components/link.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Link from './link' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/loading.js b/components/loading.js new file mode 100644 index 0000000..faf9029 --- /dev/null +++ b/components/loading.js @@ -0,0 +1,19 @@ +import {keyframes} from 'styled-components' + +const spin = keyframes` + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +` + +const Loader = styled.div` + border: 2px solid #d8edf6; + border-top: 2px solid transparent; + border-radius: 50%; + width: 25px; + height: 25px; + animation: ${spin} 1s linear infinite; + opacity: ${props => props.show ? 1 : 0} + margin: 20px auto 0; +` + +export default Loader diff --git a/components/loading.test.js b/components/loading.test.js new file mode 100644 index 0000000..8c13d55 --- /dev/null +++ b/components/loading.test.js @@ -0,0 +1,10 @@ +import React from 'react' +import Loading from './loading' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/logo.js b/components/logo.js new file mode 100644 index 0000000..bfcbc2a --- /dev/null +++ b/components/logo.js @@ -0,0 +1,8 @@ +const StyledImage = styled.img` + height: ${props => props.big ? '75px' : '50px'} + transition: height 1s; +` + +const Logo = (props) => + +export default Logo diff --git a/components/logo.test.js b/components/logo.test.js new file mode 100644 index 0000000..c32616b --- /dev/null +++ b/components/logo.test.js @@ -0,0 +1,17 @@ +import React from 'react' +import Logo from './logo' +import renderer from 'react-test-renderer' + +it('renders big version', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) + +it('renders small version', () => { + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/module.js b/components/module.js new file mode 100644 index 0000000..130e7f7 --- /dev/null +++ b/components/module.js @@ -0,0 +1,26 @@ +import Card from './card' +import Link from './link' +import Heavy from './heavy' +import Score from './score' +import Tips from './tips' + +const GridItem = styled(Card)` + width: 300px; + min-height: 150px; + position: relative; + @media (max-width: 720px) { + width: 100%; + } + border: ${props => props.valid ? 'none' : '1px solid #c0392b'} + line-height: 1.5; +` + +const Module = ({module}) => + + {module.name} + + + + + +export default Module diff --git a/components/module.test.js b/components/module.test.js new file mode 100644 index 0000000..d1fa232 --- /dev/null +++ b/components/module.test.js @@ -0,0 +1,31 @@ +import React from 'react' +import Module from './module' +import renderer from 'react-test-renderer' + +it('renders valid module', () => { + let module = { + name: 'module', + valid: true, + errors: [], + warnings: [], + suggestions: [] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) + +it('renders invalid module', () => { + let module = { + name: 'module', + valid: false, + errors: ['name is not allowed'], + warnings: [], + suggestions: [] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/score.js b/components/score.js new file mode 100644 index 0000000..2e11bb0 --- /dev/null +++ b/components/score.js @@ -0,0 +1,30 @@ +const width = 300 + 40 // width + padding + +const Bar = styled.div` + height: 5px; + width: 100%; + background: #EEE; + + position: absolute; + bottom: 0; + left: 0; + + + box-sizing: border-box; + border-left: ${props => props.score/10 * width}px solid; + border-left: ${props => props.score === 0 ? width : ''}px solid; + + border-color: ${props => props.score > 6 ? '#27ae60': '#f39c12'}; + border-color: ${props => props.score === 0 ? '#c0392b': ''}; + + border-radius: 0 0 2px 2px; +` + +const Score = ({module}) => { + let score = 10 + score -= module.warnings.length + if (module.errors.length) score = 0 + return +} + +export default Score diff --git a/components/score.test.js b/components/score.test.js new file mode 100644 index 0000000..e321c87 --- /dev/null +++ b/components/score.test.js @@ -0,0 +1,36 @@ +import React from 'react' +import Score from './score' +import renderer from 'react-test-renderer' + +it('renders correctly when module has errors', () => { + let module = { + errors: ['invalid name'], + warnings: [] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) + +it('renders correctly when score > 6 ', () => { + let module = { + errors: [], + warnings: [] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) + +it('renders correctly when score <= 6 ', () => { + let module = { + errors: [], + warnings: ['warning 1', 'warning 2', 'warning 3', 'warning 4'] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/components/tips.js b/components/tips.js new file mode 100644 index 0000000..eb037e9 --- /dev/null +++ b/components/tips.js @@ -0,0 +1,40 @@ +import pluralize from 'pluralize' +import Code from './code' + +const getTipsBlock = tips => { + return + { + tips + .map(tip => tip.replace('Missing recommended field: ', '')) + .map(tip => tip.replace('Missing optional field: ', '')) + .join(', ') + } +

+
+} + +const Tips = ({module}) =>
+ + {module.errors.length ?
+
{module.errors.length} {pluralize('error', module.errors.length)}
+ {module.errors.map((error, index) =>
- {error}
)} +
+
: ''} + + {module.warnings.length ?
+ Add recommended {pluralize('field', module.warnings.length)}: { } + {getTipsBlock(module.warnings)} +
: ''} + + {module.suggestions.length ?
+ Add optional {pluralize('field', module.suggestions.length)}: { } + {getTipsBlock(module.suggestions)} +
: ''} + + {!module.errors.length && !module.warnings.length && !module.suggestions.length ?
+ All good
💯 +
: ''} + +
+ +export default Tips diff --git a/components/tips.test.js b/components/tips.test.js new file mode 100644 index 0000000..06297f0 --- /dev/null +++ b/components/tips.test.js @@ -0,0 +1,15 @@ +import React from 'react' +import Tips from './tips' +import renderer from 'react-test-renderer' + +it('renders correctly', () => { + let module = { + errors: ['name is not valid'], + warnings: ['Missing recommended field: contributors'], + suggestions: ['Missing recommended field: engines'] + } + const tree = renderer.create( + + ).toJSON() + expect(tree).toMatchSnapshot() +}) diff --git a/index.html b/index.html deleted file mode 100644 index e6c1152..0000000 --- a/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - npm-janitor: clean up all your package.json's - - - - -
- - - diff --git a/main.js b/main.js deleted file mode 100644 index 0b0041d..0000000 --- a/main.js +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { Router, Route, Link } from 'react-router'; -import fetch from 'isomorphic-fetch'; -import Loader from 'react-loader'; -import GitHubForkRibbon from 'react-github-fork-ribbon'; -import Cards from './components/cards.jsx'; -import HotLink from './components/HotLink.jsx'; -import DebounceInput from 'react-debounce-input'; - -class App extends React.Component { - constructor (props) { - super(props); - this.state = {json: [], loaded: true}; - } - - _handleClick (name) { - this.setState({loaded: false}); - this._fetch(name); - } - - _fetch (name) { - fetch(`https://npm-janitor.herokuapp.com/api/${name.toLowerCase()}`). - then((data) => data.json()). - then((json) => this.setState({json, loaded: true})); - } - - render () { - return
- - Fork me on GitHub - - -
-

npm-janitor

-

Enter the npm user name in the search box.

-

Red -> Error, Warnings -> Yellow and Green -> Recommendations.

-
- - - - -
; - } -} - -App.contextTypes = { - router: React.PropTypes.func.isRequired -}; - -React.render(( - - - - -), document.querySelector('#content')); diff --git a/npm-janitor.gif b/npm-janitor.gif deleted file mode 100644 index b8cd060..0000000 Binary files a/npm-janitor.gif and /dev/null differ diff --git a/npm-janitor/index.html b/npm-janitor/index.html deleted file mode 100644 index e6c1152..0000000 --- a/npm-janitor/index.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - npm-janitor: clean up all your package.json's - - - - -
- - - diff --git a/package.json b/package.json index 7f65359..b33d7e4 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,52 @@ { "name": "npm-janitor-web", - "private": "true", - "browserify": { - "transform": [ - "babelify" - ] - }, + "version": "1.0.0", + "description": "", + "main": "index.js", "scripts": { - "build": "NODE_ENV=production browserify main.js | uglifyjs -cm > npm-janitor/bundle.js", - "start": "ecstatic -p 8000 npm-janitor", - "watch": "watchify main.js -o npm-janitor/bundle.js -dv" + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "eslint . && jest --coverage" + }, + "engines": { + "node": ">=7.6.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/npm-janitor/npm-janitor-web.git" }, + "keywords": [], + "author": "siddharthkp", + "license": "MIT", + "bugs": { + "url": "https://github.com/npm-janitor/npm-janitor-web/issues" + }, + "homepage": "https://github.com/npm-janitor/npm-janitor-web#readme", "dependencies": { - "babelify": "^6.1.3", - "browserify": "^10.2.6", - "ecstatic": "~0.8.0", - "history": "^1.12.1", - "isomorphic-fetch": "^2.1.1", - "react": "~0.13.3", - "react-debounce-input": "^1.1.3", - "react-github-fork-ribbon": "^0.4.1", - "react-loader": "^1.4.0", - "react-masonry-component": "^1.0.4", - "react-material-card": "0.0.2", - "react-router": "^1.0.0-rc1", - "uglify-js": "^2.4.24", - "watchify": "^3.2.3", - "whatwg-fetch": "^0.9.0" + "axios": "0.15.3", + "next": "2.0.0-beta.32", + "pluralize": "3.1.0", + "react": "15.4.2", + "react-dom": "15.4.2", + "styled-components": "1.4.3" + }, + "devDependencies": { + "babel-eslint": "7.1.1", + "babel-jest": "19.0.0", + "babel-plugin-styled-components-require": "1.0.0", + "eslint": "3.16.0", + "jest": "19.0.0" + }, + "jest": { + "collectCoverageFrom": [ + "components/**/*.js", + "pages/**/*.js" + ], + "coveragePathIgnorePatterns": [ + "/pages/_document.js", + "/pages/_global.js" + ], + "coverageDirectory": "coverage" } } diff --git a/pages/__snapshots__/index.test.js.snap b/pages/__snapshots__/index.test.js.snap new file mode 100644 index 0000000..e83a552 --- /dev/null +++ b/pages/__snapshots__/index.test.js.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` +
+
+
+ +
+ + + ↵ + +
+
+
+
+
+`; diff --git a/pages/__snapshots__/user.test.js.snap b/pages/__snapshots__/user.test.js.snap new file mode 100644 index 0000000..486bea2 --- /dev/null +++ b/pages/__snapshots__/user.test.js.snap @@ -0,0 +1,131 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders with modules 1`] = ` +
+
+
+ +
+ + + ↵ + +
+
+
+
+
+ + +
+ npm-janitor-web +
+
+
+ +
+ Add recommended + field + : + + + Add recommended field: contributors + +
+
+
+
+
+ Add optional + field + : + + + Add optional field: homepage + +
+
+
+
+
+
+ + +
+
+`; + +exports[`renders without modules 1`] = ` +
+
+
+ +
+ + + ↵ + +
+
+
+
+
+ +
+
+`; diff --git a/pages/_document.js b/pages/_document.js new file mode 100644 index 0000000..2ce9c24 --- /dev/null +++ b/pages/_document.js @@ -0,0 +1,36 @@ +import Document, {Head, Main, NextScript} from 'next/document' +import styleSheet from 'styled-components/lib/models/StyleSheet' +import {injectGlobal} from 'styled-components' +import React from 'react' +import {styles, Head as _Head} from './_global' + +let stylesInjected = false + +export default class MyDocument extends Document { + static async getInitialProps ({renderPage}) { + if (!stylesInjected) { + injectGlobal`${styles}` + stylesInjected = true + } + const page = renderPage() + + const style = styleSheet.rules().map(rule => rule.cssText).join('\n') + + return {...page, style} + } + + render () { + return ( + + <_Head/> + +