diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 918bf8b3..a5e3c84f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -17,6 +17,7 @@ jobs: - name: Install and lint run: | npm ci + npm run test:other npm run test:code env: CI: true diff --git a/.lintstagedrc.js b/.lintstagedrc.js index 87a02c84..18e1d218 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -7,9 +7,8 @@ const cli = new CLIEngine({}); module.exports = { "*.{js,ts,tsx}": files => { return ( - "eslint --max-warnings=0 " + - files.filter(file => !cli.isPathIgnored(file)).join(" ") + "eslint --fix" + files.filter(file => !cli.isPathIgnored(file)).join(" ") ); }, - "*.{js,css,scss,json,md,ts,tsx,html,yml}": ["prettier --write"], + "*.{css,scss,json,md,html,yml}": ["prettier --write"], }; diff --git a/package-lock.json b/package-lock.json index c6dc04f4..1fc882da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5299,6 +5299,23 @@ } } }, + "eslint-config-prettier": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, "eslint-config-react-app": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.1.0.tgz", @@ -5583,6 +5600,15 @@ } } }, + "eslint-plugin-prettier": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.16.0.tgz", @@ -5976,6 +6002,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", @@ -12119,6 +12151,15 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", diff --git a/package.json b/package.json index ea808041..26fed6fa 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,9 @@ "@types/nanoid": "2.1.0", "@types/react": "16.9.19", "@types/react-dom": "16.9.5", + "eslint": "6.8.0", + "eslint-config-prettier": "6.10.0", + "eslint-plugin-prettier": "3.1.2", "husky": "4.2.1", "lint-staged": "10.0.3", "node-sass": "4.13.1", @@ -28,10 +31,17 @@ "typescript": "3.7.5" }, "eslintConfig": { - "extends": "react-app", + "extends": [ + "prettier", + "react-app" + ], + "plugins": [ + "prettier" + ], "rules": { + "curly": "error", "no-console": [ - "warn", + "error", { "allow": [ "warn", @@ -39,7 +49,17 @@ "info" ] } - ] + ], + "no-else-return": "error", + "no-useless-return": "error", + "prefer-const": [ + "error", + { + "destructuring": "all" + } + ], + "prefer-template": "error", + "prettier/prettier": "error" } }, "homepage": "https://excalidraw.com", @@ -54,12 +74,15 @@ "build": "react-scripts build", "build-node": "./scripts/build-node.js", "eject": "react-scripts eject", - "fix": "npm run prettier -- --write", - "prettier": "prettier \"**/*.{js,css,scss,json,md,ts,tsx,html,yml}\" --ignore-path=.eslintignore", + "fix": "npm run fix:other && npm run fix:code", + "fix:code": "npm run test:code -- --fix", + "fix:other": "npm run prettier -- --write", + "prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore", "start": "react-scripts start", "test": "npm run test:app", "test:app": "react-scripts test --env=jsdom --passWithNoTests", - "test:code": "npm run prettier -- --list-different" + "test:code": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", + "test:other": "npm run prettier -- --list-different" }, "version": "1.0.0", "license": "MIT", diff --git a/src/actions/manager.tsx b/src/actions/manager.tsx index ace170d9..399fd70d 100644 --- a/src/actions/manager.tsx +++ b/src/actions/manager.tsx @@ -37,7 +37,9 @@ export class ActionManager implements ActionsManagerInterface { action => action.keyTest && action.keyTest(event, appState, elements), ); - if (data.length === 0) return null; + if (data.length === 0) { + return null; + } event.preventDefault(); return data[0].perform(elements, appState, null); diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 7e670e8d..d505f0a3 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -109,7 +109,9 @@ const Picker = function({
{ - if (el) gallery.current = el; + if (el) { + gallery.current = el; + } }} > {colors.map((_color, i) => ( @@ -124,8 +126,12 @@ const Picker = function({ style={{ backgroundColor: _color }} key={_color} ref={el => { - if (el && i === 0) firstItem.current = el; - if (el && _color === color) activeItem.current = el; + if (el && i === 0) { + firstItem.current = el; + } + if (el && _color === color) { + activeItem.current = el; + } }} onFocus={() => { onChange(_color); @@ -186,7 +192,7 @@ const ColorInput = React.forwardRef( onChange={e => { const value = e.target.value.toLowerCase(); if (value.match(colorRegex)) { - onChange(value === "transparent" ? "transparent" : "#" + value); + onChange(value === "transparent" ? "transparent" : `#${value}`); } setInnerValue(value); }} diff --git a/src/components/ExportDialog.tsx b/src/components/ExportDialog.tsx index 44589412..7eeaa895 100644 --- a/src/components/ExportDialog.tsx +++ b/src/components/ExportDialog.tsx @@ -178,7 +178,7 @@ function ExportModal({ key={s} size="s" type="radio" - icon={"x" + s} + icon={`x${s}`} name="export-canvas-scale" aria-label={`Scale ${s} x`} id="export-canvas-scale" diff --git a/src/components/FixedSideContainer.tsx b/src/components/FixedSideContainer.tsx index 8a49de51..cc37144a 100644 --- a/src/components/FixedSideContainer.tsx +++ b/src/components/FixedSideContainer.tsx @@ -12,7 +12,7 @@ export function FixedSideContainer({ side, }: FixedSideContainerProps) { return ( -
+
{children}
); diff --git a/src/components/Popover.tsx b/src/components/Popover.tsx index 7b76af14..6e62e03f 100644 --- a/src/components/Popover.tsx +++ b/src/components/Popover.tsx @@ -26,11 +26,11 @@ export function Popover({ const viewportWidth = window.innerWidth; if (x + width > viewportWidth) { - element.style.left = viewportWidth - width + "px"; + element.style.left = `${viewportWidth - width}px`; } const viewportHeight = window.innerHeight; if (y + height > viewportHeight) { - element.style.top = viewportHeight - height + "px"; + element.style.top = `${viewportHeight - height}px`; } } }, [fitInViewport]); @@ -42,7 +42,9 @@ export function Popover({ onClick={onCloseRequest} onContextMenu={e => { e.preventDefault(); - if (onCloseRequest) onCloseRequest(); + if (onCloseRequest) { + onCloseRequest(); + } }} /> {children} diff --git a/src/components/ProjectName.tsx b/src/components/ProjectName.tsx index f13d154a..9696ab45 100644 --- a/src/components/ProjectName.tsx +++ b/src/components/ProjectName.tsx @@ -16,7 +16,9 @@ export class ProjectName extends Component { private handleBlur = (e: React.FocusEvent) => { const value = e.currentTarget.innerText.trim(); - if (value !== this.props.value) this.props.onChange(value); + if (value !== this.props.value) { + this.props.onChange(value); + } removeSelection(); }; diff --git a/src/components/ToolButton.tsx b/src/components/ToolButton.tsx index 120a3f83..62c89c0b 100644 --- a/src/components/ToolButton.tsx +++ b/src/components/ToolButton.tsx @@ -35,7 +35,7 @@ export const ToolButton = React.forwardRef(function( React.useImperativeHandle(ref, () => innerRef.current); const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`; - if (props.type === "button") + if (props.type === "button") { return ( ); + } return (