diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d894db377c..d3c8731ca2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,6 @@ repos: files: "assets/js" additional_dependencies: - eslint@7.2.0 - - eslint-config-airbnb@18.2.0 - eslint-plugin-import@2.22.1 - eslint-plugin-jsx-a11y@6.4.1 - eslint-plugin-react@7.21.5 diff --git a/assets/.eslintrc.json b/assets/.eslintrc.json index 5893523203..eccc8d3f0a 100644 --- a/assets/.eslintrc.json +++ b/assets/.eslintrc.json @@ -2,7 +2,11 @@ "env": { "browser": true }, - "extends": ["airbnb", "prettier"], + "extends": ["eslint:recommended", + "plugin:react/recommended", + "prettier" + ], + "parser": "babel-eslint", "plugins": ["prettier"], "rules": { "max-len": [0, {"code": 120}], @@ -11,6 +15,7 @@ "react/destructuring-assignment": [0], "react/prop-types": [0], "max-classes-per-file": [0], + "react/display-name": [0], "react/jsx-one-expression-per-line": [0], "react/self-closing-comp": [0], "no-unused-expressions": [1, { "allowShortCircuit": true }], @@ -18,5 +23,14 @@ "jsx-a11y/click-events-have-key-events": [0], "jsx-a11y/no-static-element-interactions": [0], "react/no-did-update-set-state": [0] + }, + "settings": { + "react": { + "createClass": "createReactClass", // Regex for Component Factory to use, + // default to "createReactClass" + "pragma": "React", // Pragma to use, default to "React" + "fragment": "Fragment", // Fragment to use (may be a property of ), default to "Fragment" + "version": "detect" + } } } diff --git a/assets/.prettierignore b/assets/.prettierignore new file mode 100644 index 0000000000..3c40ba6b1d --- /dev/null +++ b/assets/.prettierignore @@ -0,0 +1,6 @@ +node_modules/ +static/images/ +.*.json +.*rc +*.json +*.config.js diff --git a/assets/js/app.js b/assets/js/app.js index 9a2ea9ac16..9c2bb174d5 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -48,7 +48,7 @@ if (registerForm) { registerForm.submit(); } } - + /* eslint-disable-next-line no-undef */ plausible('Signup', {callback: submitForm}); }) } @@ -94,7 +94,7 @@ function showChangelogNotification(el) { const embedButton = document.getElementById('generate-embed') if (embedButton) { - embedButton.addEventListener('click', function(e) { + embedButton.addEventListener('click', function(_e) { const baseUrl = document.getElementById('base-url').value const embedCode = document.getElementById('embed-code') const theme = document.getElementById('theme').value.toLowerCase() diff --git a/assets/js/dashboard/api.js b/assets/js/dashboard/api.js index cccacbdafb..5257eed734 100644 --- a/assets/js/dashboard/api.js +++ b/assets/js/dashboard/api.js @@ -13,6 +13,7 @@ class ApiError extends Error { function serialize(obj) { var str = []; for (var p in obj) + /* eslint-disable-next-line no-prototype-builtins */ if (obj.hasOwnProperty(p)) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p])); } diff --git a/assets/js/dashboard/query.js b/assets/js/dashboard/query.js index 2a77b084e3..857fc3bc61 100644 --- a/assets/js/dashboard/query.js +++ b/assets/js/dashboard/query.js @@ -47,7 +47,7 @@ export function parseQuery(querystring, site) { export function appliedFilters(query) { return Object.keys(query.filters) .map((key) => [key, query.filters[key]]) - .filter(([key, value]) => !!value); + .filter(([_key, value]) => !!value); } function generateQueryString(data) { @@ -89,7 +89,7 @@ class QueryLink extends React.Component { } render() { - const { history, query, to, ...props } = this.props + const { to, ...props } = this.props return ( + ) diff --git a/assets/js/dashboard/stats/countries.js b/assets/js/dashboard/stats/countries.js index 96b02e5253..2c06ae6847 100644 --- a/assets/js/dashboard/stats/countries.js +++ b/assets/js/dashboard/stats/countries.js @@ -1,11 +1,11 @@ import React from 'react'; import Datamap from 'datamaps' import { withRouter } from 'react-router-dom' +import * as d3 from "d3" import numberFormatter from '../number-formatter' import FadeIn from '../fade-in' import LazyLoader from '../lazy-loader' -import Bar from './bar' import MoreLink from './more-link' import * as api from '../api' import { navigateToQuery } from '../query' @@ -128,7 +128,7 @@ class Countries extends React.Component { geolocationDbNotice() { if (this.props.site.selfhosted) { return ( - IP Geolocation by DB-IP + IP Geolocation by DB-IP ) } } diff --git a/assets/js/dashboard/stats/devices/index.js b/assets/js/dashboard/stats/devices/index.js index bc55d756fd..e2905746fc 100644 --- a/assets/js/dashboard/stats/devices/index.js +++ b/assets/js/dashboard/stats/devices/index.js @@ -22,19 +22,19 @@ const EXPLANATION = { function iconFor(screenSize) { if (screenSize === 'Mobile') { return ( - + ) } else if (screenSize === 'Tablet') { return ( - + ) } else if (screenSize === 'Laptop') { return ( - + ) } else if (screenSize === 'Desktop') { return ( - + ) } } diff --git a/assets/js/dashboard/stats/modals/entry-pages.js b/assets/js/dashboard/stats/modals/entry-pages.js index 05104849d0..ddb6b3a51a 100644 --- a/assets/js/dashboard/stats/modals/entry-pages.js +++ b/assets/js/dashboard/stats/modals/entry-pages.js @@ -24,7 +24,7 @@ class EntryPagesModal extends React.Component { } loadPages() { - const {query, page, pages} = this.state; + const {query, page} = this.state; api.get( `/api/stats/${encodeURIComponent(this.props.site.domain)}/entry-pages`, diff --git a/assets/js/dashboard/stats/modals/exit-pages.js b/assets/js/dashboard/stats/modals/exit-pages.js index 9dff307514..d0eb0961b4 100644 --- a/assets/js/dashboard/stats/modals/exit-pages.js +++ b/assets/js/dashboard/stats/modals/exit-pages.js @@ -24,7 +24,7 @@ class ExitPagesModal extends React.Component { } loadPages() { - const {query, page, pages} = this.state; + const {query, page} = this.state; api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/exit-pages`, query, {limit: 100, page}) .then((res) => this.setState((state) => ({loading: false, pages: state.pages.concat(res), moreResultsAvailable: res.length === 100}))) diff --git a/assets/js/dashboard/stats/modals/google-keywords.js b/assets/js/dashboard/stats/modals/google-keywords.js index 268550f880..0dcf4b6245 100644 --- a/assets/js/dashboard/stats/modals/google-keywords.js +++ b/assets/js/dashboard/stats/modals/google-keywords.js @@ -4,7 +4,6 @@ import { Link, withRouter } from 'react-router-dom' import Modal from './modal' import * as api from '../../api' import numberFormatter from '../../number-formatter' -import Bar from '../bar' import {parseQuery, toHuman} from '../../query' import RocketIcon from './rocket-icon' diff --git a/assets/js/dashboard/stats/modals/pages.js b/assets/js/dashboard/stats/modals/pages.js index 17e945121c..7208974da1 100644 --- a/assets/js/dashboard/stats/modals/pages.js +++ b/assets/js/dashboard/stats/modals/pages.js @@ -25,7 +25,7 @@ class PagesModal extends React.Component { loadPages() { const detailed = this.showExtra() - const {query, page, pages} = this.state; + const {query, page} = this.state; api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/pages`, query, {limit: 100, page, detailed}) .then((res) => this.setState((state) => ({loading: false, pages: state.pages.concat(res), moreResultsAvailable: res.length === 100}))) diff --git a/assets/js/dashboard/stats/modals/referrer-drilldown.js b/assets/js/dashboard/stats/modals/referrer-drilldown.js index aff3c236de..e97a25ffe4 100644 --- a/assets/js/dashboard/stats/modals/referrer-drilldown.js +++ b/assets/js/dashboard/stats/modals/referrer-drilldown.js @@ -62,7 +62,7 @@ class ReferrerDrilldownModal extends React.Component { renderExternalLink(name) { if (name !== 'Direct / None') { return ( - + ) @@ -92,14 +92,14 @@ class ReferrerDrilldownModal extends React.Component { return (
- +
{tweet.author_name}
@{tweet.author_handle}
- +
diff --git a/assets/js/dashboard/stats/modals/sources.js b/assets/js/dashboard/stats/modals/sources.js index 944d1f331b..d74aa9b3ce 100644 --- a/assets/js/dashboard/stats/modals/sources.js +++ b/assets/js/dashboard/stats/modals/sources.js @@ -1,7 +1,6 @@ import React from "react"; import { Link, withRouter } from 'react-router-dom' -import FadeIn from '../../fade-in' import Modal from './modal' import * as api from '../../api' import numberFormatter, {durationFormatter} from '../../number-formatter' diff --git a/assets/js/dashboard/stats/sources/referrer-list.js b/assets/js/dashboard/stats/sources/referrer-list.js index 23e60014f0..5e32a5579b 100644 --- a/assets/js/dashboard/stats/sources/referrer-list.js +++ b/assets/js/dashboard/stats/sources/referrer-list.js @@ -62,7 +62,7 @@ export default class Referrers extends React.Component { renderExternalLink(referrer) { if (this.props.query.filters.source && this.props.query.filters.source !== 'Google' && referrer.name !== 'Direct / None') { return ( - + ) diff --git a/assets/js/dashboard/stats/sources/search-terms.js b/assets/js/dashboard/stats/sources/search-terms.js index 9ee3e7d40e..6c5c989acb 100644 --- a/assets/js/dashboard/stats/sources/search-terms.js +++ b/assets/js/dashboard/stats/sources/search-terms.js @@ -91,7 +91,7 @@ export default class SearchTerms extends React.Component {
Could not find any search terms for this period
Google Search Console data is sampled and delayed by 24-36h
-
Read more on our documentation
+
Read more on our documentation
) } diff --git a/assets/js/dashboard/stats/visitor-graph.js b/assets/js/dashboard/stats/visitor-graph.js index a6c6dc145f..6fe0ba508e 100644 --- a/assets/js/dashboard/stats/visitor-graph.js +++ b/assets/js/dashboard/stats/visitor-graph.js @@ -1,7 +1,7 @@ import React from 'react'; import { withRouter } from 'react-router-dom' import Chart from 'chart.js/auto'; -import { eventName, navigateToQuery } from '../query' +import { navigateToQuery } from '../query' import numberFormatter, {durationFormatter} from '../number-formatter' import * as api from '../api' import {ThemeContext} from '../theme-context' @@ -67,7 +67,7 @@ const DAYS_ABBREV = [ ] function dateFormatter(interval, longForm) { - return function(isoDate, index, ticks) { + return function(isoDate, _index, _ticks) { let date = new Date(isoDate) if (interval === 'month') { @@ -147,7 +147,7 @@ class LineGraph extends React.Component { return ` ${item.formattedValue} ${pluralizedLabel}` } }, - footer: function(dataPoints) { + footer: function(_dataPoints) { if (graphData.interval === 'month') { return 'Click to view month' } else if (graphData.interval === 'date') { @@ -178,7 +178,7 @@ class LineGraph extends React.Component { grid: {display: false}, ticks: { maxTicksLimit: 8, - callback: function(val, index, ticks) { return dateFormatter(graphData.interval)(this.getLabelForValue(val)) }, + callback: function(val, _index, _ticks) { return dateFormatter(graphData.interval)(this.getLabelForValue(val)) }, color: this.props.darkTheme ? 'rgb(243, 244, 246)' : undefined } } @@ -345,7 +345,7 @@ class LineGraph extends React.Component { } } -LineGraph = withRouter(LineGraph) +const LineGraphWithRouter = withRouter(LineGraph) export default class VisitorGraph extends React.Component { constructor(props) { @@ -379,7 +379,7 @@ export default class VisitorGraph extends React.Component { return ( {theme => ( - + )} ) diff --git a/assets/js/dashboard/theme-provider-hoc.js b/assets/js/dashboard/theme-provider-hoc.js index df4a9c0f32..0360d0ff2a 100644 --- a/assets/js/dashboard/theme-provider-hoc.js +++ b/assets/js/dashboard/theme-provider-hoc.js @@ -9,7 +9,7 @@ export const withThemeProvider = (WrappedComponent) => { dark: document.querySelector('html').classList.contains('dark') || false }; - this.mutationObserver = new MutationObserver((mutationsList, observer) => { + this.mutationObserver = new MutationObserver((mutationsList, _observer) => { mutationsList.forEach(mutation => { if (mutation.attributeName === 'class') { this.setState({ dark: mutation.target.classList.contains('dark') }); diff --git a/assets/js/polyfills/closest.js b/assets/js/polyfills/closest.js index 0d21966fd0..64f4823668 100644 --- a/assets/js/polyfills/closest.js +++ b/assets/js/polyfills/closest.js @@ -6,6 +6,7 @@ if (window.Element && !Element.prototype.closest) { el = this; do { i = matches.length; + // eslint-disable-next-line no-empty while (--i >= 0 && matches.item(i) !== el) {}; } while ((i < 0) && (el = el.parentElement)); return el; diff --git a/assets/package-lock.json b/assets/package-lock.json index f20ba57662..fc4f6332b8 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -47,8 +47,8 @@ "webpack-cli": "^4.7.0" }, "devDependencies": { + "babel-eslint": "^10.1.0", "eslint": "^7.2.0", - "eslint-config-airbnb": "^18.2.0", "eslint-config-prettier": "^7.0.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1", @@ -62,11 +62,11 @@ } }, "../deps/phoenix": { - "version": "1.5.8", + "version": "1.4.17", "license": "MIT" }, "../deps/phoenix_html": { - "version": "2.14.3" + "version": "2.14.2" }, "node_modules/@babel/code-frame": { "version": "7.12.13", @@ -2296,6 +2296,36 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" + } + }, + "node_modules/babel-eslint/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/babel-loader": { "version": "8.2.2", "license": "MIT", @@ -2737,11 +2767,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/confusing-browser-globals": { - "version": "1.0.10", - "dev": true, - "license": "MIT" - }, "node_modules/contains-path": { "version": "0.1.0", "dev": true, @@ -3646,43 +3671,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb": { - "version": "18.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-config-airbnb-base": "^14.2.1", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-jsx-a11y": "^6.4.1", - "eslint-plugin-react": "^7.21.5", - "eslint-plugin-react-hooks": "^4 || ^3 || ^2.3.0 || ^1.7.0" - } - }, - "node_modules/eslint-config-airbnb-base": { - "version": "14.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", - "eslint-plugin-import": "^2.22.1" - } - }, "node_modules/eslint-config-prettier": { "version": "7.0.0", "dev": true, @@ -11200,6 +11188,28 @@ "version": "2.2.0", "dev": true }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "babel-loader": { "version": "8.2.2", "requires": { @@ -11480,10 +11490,6 @@ "typedarray": "^0.0.6" } }, - "confusing-browser-globals": { - "version": "1.0.10", - "dev": true - }, "contains-path": { "version": "0.1.0", "dev": true @@ -12190,24 +12196,6 @@ } } }, - "eslint-config-airbnb": { - "version": "18.2.1", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^14.2.1", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - } - }, - "eslint-config-airbnb-base": { - "version": "14.2.1", - "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.2" - } - }, "eslint-config-prettier": { "version": "7.0.0", "dev": true, diff --git a/assets/package.json b/assets/package.json index 75056e3f0c..a96f1ffd9e 100644 --- a/assets/package.json +++ b/assets/package.json @@ -3,7 +3,9 @@ "license": "MIT", "scripts": { "deploy": "$(npm bin)/webpack --mode production", - "watch": "$(npm bin)/webpack --mode development --watch" + "watch": "$(npm bin)/webpack --mode development --watch", + "format": "$(npm bin)/prettier --write {css,js}/**", + "lint": "$(npm bin)/eslint js/**" }, "dependencies": { "@babel/core": "^7.14.3", @@ -47,8 +49,8 @@ "webpack-cli": "^4.7.0" }, "devDependencies": { + "babel-eslint": "^10.1.0", "eslint": "^7.2.0", - "eslint-config-airbnb": "^18.2.0", "eslint-config-prettier": "^7.0.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jsx-a11y": "^6.4.1",