diff --git a/.eslintrc.json b/.eslintrc.json index 8932acc..99531ad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,49 +1,36 @@ { "env": { - "browser": true, - "node": true, "es6": true }, "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "settings": { - "import/resolver": { - "node": { - "extensions": [".js", ".ts"] - } - } - }, + "parserOptions": {"project": ["./tsconfig.json"]}, "plugins": ["@typescript-eslint"], - "extends": "airbnb-base", + "extends": [ + "xo/browser", + "xo-typescript" + ], "rules": { - "indent": ["error", "tab"], - "no-tabs": "off", - "object-curly-spacing": ["error", "never"], - "no-param-reassign": ["error", {"props": false}], - "import/extensions": ["error", "ignorePackages", { - "js": "never", - "ts": "never" - }] - }, - "overrides": [ - { - "files": ["*.ts"], - "parser": "@typescript-eslint/parser", - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "rules": { - "indent": "off", - "max-len": [2, 120], - "import/prefer-default-export": "off", - "@typescript-eslint/indent": ["error", "tab"], - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-non-null-assertion": "off" + "arrow-parens": ["error", "always"], + "curly": ["error", "multi-line"], + "padding-line-between-statements": "off", + "object-shorthand": ["error", "properties"], + "@typescript-eslint/ban-types": ["error", {"extendDefaults": true}], + "@typescript-eslint/member-delimiter-style": ["error", { + "multiline": { + "delimiter": "comma", + "requireLast": true + }, + "singleline": { + "delimiter": "comma", + "requireLast": false } + }], + "@typescript-eslint/padding-line-between-statements": "off" + }, + "overrides": [{ + "files": ["test/*.ts"], + "rules": { + "@typescript-eslint/no-confusing-void-expression": "off" } - ] + }] } diff --git a/.gitignore b/.gitignore index de4d1f0..6dec33a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ dist node_modules +.eslintcache diff --git a/jest.config.ts b/jest.config.ts index eb9b500..fb997cc 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/naming-convention */ const reporters = ['default']; module.exports = { diff --git a/package-lock.json b/package-lock.json index 6f163d0..37b3b29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@babel/preset-env": "^7.22.10", "@babel/preset-typescript": "^7.22.5", "@testing-library/dom": "^9.3.1", - "@testing-library/jest-dom": "^6.0.1", + "@testing-library/jest-dom": "^6.1.6", "@types/html-webpack-plugin": "^3.2.6", "@types/jest": "^29.5.3", "@types/pug": "^2.0.6", @@ -28,9 +28,9 @@ "css-loader": "^6.8.1", "cssnano": "^6.0.1", "eslint": "^8.47.0", - "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-xo": "^0.43.1", + "eslint-config-xo-typescript": "^1.0.1", "eslint-plugin-import": "^2.28.1", - "eslint-webpack-plugin": "^4.0.1", "gh-pages": "^6.0.0", "html-webpack-plugin": "^5.5.3", "jest": "^29.6.2", @@ -45,7 +45,6 @@ "pug": "^3.0.2", "stylelint": "^14.16.1", "stylelint-config-standard": "^29.0.0", - "stylelint-webpack-plugin": "^4.1.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "^5.1.6", @@ -64,9 +63,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", - "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -3786,12 +3785,12 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.1.4.tgz", - "integrity": "sha512-wpoYrCYwSZ5/AxcrjLxJmCU6I5QAJXslEeSiMQqaWmP2Kzpd1LvF/qxmAIW2qposULGWq2gw30GgVNFLSc2Jnw==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.1.6.tgz", + "integrity": "sha512-YwuiOdYEcxhfC2u5iNKlvg2Q5MgbutovP6drq7J1HrCbvR+G58BbtoCoq+L/kNlrNFsu2Kt3jaFAviLVxYHJZg==", "dev": true, "dependencies": { - "@adobe/css-tools": "^4.3.1", + "@adobe/css-tools": "^4.3.2", "@babel/runtime": "^7.9.2", "aria-query": "^5.0.0", "chalk": "^3.0.0", @@ -7507,23 +7506,40 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/eslint-config-xo": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.43.1.tgz", + "integrity": "sha512-azv1L2PysRA0NkZOgbndUpN+581L7wPqkgJOgxxw3hxwXAbJgD6Hqb/SjHRiACifXt/AvxCzE/jIKFAlI7XjvQ==", "dev": true, "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" + "confusing-browser-globals": "1.0.11" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" }, "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "eslint": ">=8.27.0" + } + }, + "node_modules/eslint-config-xo-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-xo-typescript/-/eslint-config-xo-typescript-1.0.1.tgz", + "integrity": "sha512-vPQssnRSUgBFOEfB/KY12CXwltwFSn4RSCfa+w7gjBC2PFQ7Yfgmyei+1XUZ3K+8LRGef2NMJUcxts7PldhDjg==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": ">=6.0.0", + "@typescript-eslint/parser": ">=6.0.0", + "eslint": ">=8.0.0", + "typescript": ">=4.7" } }, "node_modules/eslint-import-resolver-node": { @@ -7652,30 +7668,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-fUFcXpui/FftGx3NzvWgLZXlLbu+m74sUxGEgxgoxYcUtkIQbS6SdNNZkS99m5ycb23TfoNYrDpp1k/CK5j6Hw==", - "dev": true, - "dependencies": { - "@types/eslint": "^8.37.0", - "jest-worker": "^29.5.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^8.0.0", - "webpack": "^5.0.0" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -12701,20 +12693,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/object.fromentries": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", @@ -16047,30 +16025,6 @@ "stylelint": "^14.14.0" } }, - "node_modules/stylelint-webpack-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-4.1.1.tgz", - "integrity": "sha512-yOyd2AfrxfawxKDememazGVJX2vMq9o11E6HvBu4+SKvgK3ZulkjpYdI1muBTxItwoxH2UmfIZzQM+/M5V3kTQ==", - "dev": true, - "dependencies": { - "globby": "^11.1.0", - "jest-worker": "^29.5.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "stylelint": "^13.0.0 || ^14.0.0 || ^15.0.0", - "webpack": "^5.0.0" - } - }, "node_modules/stylelint/node_modules/@csstools/selector-specificity": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", diff --git a/package.json b/package.json index e446278..546c2f5 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,10 @@ "scripts": { "start": "webpack-dev-server --mode=development", "test": "jest", + "typecheck": "npx tsc", + "lint:js": "npx eslint . --ext .ts --cache", + "lint:css": "npx stylelint \"src/**/*.css\"", + "lint": "npm run lint:js; npm run lint:css", "build": "webpack --mode=production", "deploy": "gh-pages -d dist" }, @@ -26,7 +30,7 @@ "@babel/preset-env": "^7.22.10", "@babel/preset-typescript": "^7.22.5", "@testing-library/dom": "^9.3.1", - "@testing-library/jest-dom": "^6.0.1", + "@testing-library/jest-dom": "^6.1.6", "@types/html-webpack-plugin": "^3.2.6", "@types/jest": "^29.5.3", "@types/pug": "^2.0.6", @@ -38,9 +42,9 @@ "css-loader": "^6.8.1", "cssnano": "^6.0.1", "eslint": "^8.47.0", - "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-xo": "^0.43.1", + "eslint-config-xo-typescript": "^1.0.1", "eslint-plugin-import": "^2.28.1", - "eslint-webpack-plugin": "^4.0.1", "gh-pages": "^6.0.0", "html-webpack-plugin": "^5.5.3", "jest": "^29.6.2", @@ -55,7 +59,6 @@ "pug": "^3.0.2", "stylelint": "^14.16.1", "stylelint-config-standard": "^29.0.0", - "stylelint-webpack-plugin": "^4.1.1", "ts-jest": "^29.1.1", "ts-node": "^10.9.1", "typescript": "^5.1.6", diff --git a/src/data.ts b/src/data.ts index 77b6bdd..46e4da8 100644 --- a/src/data.ts +++ b/src/data.ts @@ -1,4 +1,4 @@ -import { +import type { PastSupport, Committee, Constituency, diff --git a/src/display.ts b/src/display.ts index e277e08..d2e577c 100644 --- a/src/display.ts +++ b/src/display.ts @@ -61,7 +61,7 @@ const displayUrl = (support: number[]) => { if (s > 0) searchParams.append(committees[i].id, s.toString()); }); const urlWithoutSearchString = location.href.split('?')[0]; - const url = `${urlWithoutSearchString}?${searchParams}`; + const url = `${urlWithoutSearchString}?${searchParams.toString()}`; document.getElementById('url')!.innerHTML = `Link do wyników: ${url.link(url)}`; } }; @@ -147,7 +147,7 @@ const displayConstituencyResults = () => { const data = (constituency.mandates && constituency.support) ? constituency.mandates.map((mandates, committeeIndex) => ({ committee: committees[committeeIndex], - support: (constituency.support as number[])[committeeIndex], + support: (constituency.support!)[committeeIndex], mandates, })) : []; @@ -164,7 +164,7 @@ const displayConstituencyResults = () => { const pathElement = event.target as SVGPathElement; pathElement.style.stroke = '#444'; constituencyNumber.innerHTML = `Okręg nr ${pathElement.dataset.cid}`; - constituencyName.innerHTML = pathElement.dataset.cname as string; + constituencyName.innerHTML = pathElement.dataset.cname!; }); path.addEventListener('mouseout', (event) => { const pathElement = event.target as SVGPathElement; @@ -187,7 +187,9 @@ const displayConstituencyResults = () => { }; const validate = (form: HTMLFormElement, inputs: NodeListOf, support: number[]) => { - inputs.forEach((input) => input.setCustomValidity('')); + inputs.forEach((input) => { + input.setCustomValidity(''); + }); support.some((inputValue, index) => { if (inputValue < 0) { @@ -225,9 +227,11 @@ export const calculate = (): void => { pieChart = displayPieChart(mandates); displayConstituencyResults(); - inputs.forEach((input) => input.addEventListener('input', () => { - clearResults(); - })); + inputs.forEach((input) => { + input.addEventListener('input', () => { + clearResults(); + }); + }); }; export const generateTable = (): void => { diff --git a/src/mandates.ts b/src/mandates.ts index 25d0bcf..80e67c5 100644 --- a/src/mandates.ts +++ b/src/mandates.ts @@ -1,5 +1,5 @@ import {committees, constituencies, pastSupport} from './data'; -import {Constituency} from './types'; +import type {Constituency} from './types'; const calculateLocalSupport = ( support: number[], @@ -31,7 +31,7 @@ export default (support: number[]): number[] => { .map((pastCommittee) => pastSupport[pastCommittee[0]] * pastCommittee[1]) .reduce((a, b) => a + b, 0) )); - const mandates: number[] = new Array(support.length + 1).fill(0); + const mandates = new Array(support.length + 1).fill(0) as number[]; constituencies.forEach((constituency) => { const localSupport = calculateLocalSupport(support, pastSupportProjection, constituency); constituency.support = localSupport; @@ -40,7 +40,7 @@ export default (support: number[]): number[] => { if (support[index] < committees[index].threshold) return 0; return localCommitteeSupport; }); - const quotients: {quotient: number; committeeIndex: number}[] = []; + const quotients: Array<{quotient: number, committeeIndex: number}> = []; for (let divisor = 1; divisor <= constituency.size; divisor += 1) { for (let committeeIndex = 0; committeeIndex < localSupport.length; committeeIndex += 1) { quotients.push({ diff --git a/src/pug.ts b/src/pug.ts index 1eaf83f..865a0bb 100644 --- a/src/pug.ts +++ b/src/pug.ts @@ -1,5 +1,5 @@ declare module '*.pug' { - import pug = require('pug'); + import type pug from 'pug'; const content: pug.compileTemplate; export = content; diff --git a/src/types.ts b/src/types.ts index e0f6f04..38c2faf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,21 +1,21 @@ -type PastCommiteeId = 'pis' | 'ko' | 'td' | 'lewica' | 'konfederacja' +type PastCommiteeId = 'pis' | 'ko' | 'td' | 'lewica' | 'konfederacja'; export type PastSupport = { [pastCommitteeId in PastCommiteeId]: number; -} +}; export type Committee = { - id: string; - name: string; - shortName: string; - threshold: number; - pastSupportEquivalence: [PastCommiteeId, number][]; -} + id: string, + name: string, + shortName: string, + threshold: number, + pastSupportEquivalence: Array<[PastCommiteeId, number]>, +}; export type Constituency = { - name: string; - size: number; - pastSupport: PastSupport; - support?: number[]; - mandates?: number[]; -} + name: string, + size: number, + pastSupport: PastSupport, + support?: number[], + mandates?: number[], +}; diff --git a/test/matchMedia.mock.ts b/test/matchMedia.mock.ts index b46f3d1..18a365b 100644 --- a/test/matchMedia.mock.ts +++ b/test/matchMedia.mock.ts @@ -1,4 +1,4 @@ -window.matchMedia = jest.fn().mockImplementation((query) => ({ +window.matchMedia = jest.fn().mockImplementation((query: string) => ({ matches: false, media: query, onchange: null, diff --git a/tsconfig.json b/tsconfig.json index e2519d0..bc12cae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "node", "outDir": "./dist/", "removeComments": true, + "skipLibCheck": true, "sourceMap": true, "strict": true, "target": "ES5" diff --git a/webpack.config.ts b/webpack.config.ts index 059dd8f..20c32cb 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,9 +1,6 @@ -/* eslint-disable import/no-extraneous-dependencies */ import path from 'path'; -import ESLintPlugin from 'eslint-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; -import StyleLintPlugin from 'stylelint-webpack-plugin'; export default { target: 'web', @@ -63,14 +60,6 @@ export default { filename: '[name].css', chunkFilename: '[id].css', }), - new ESLintPlugin({ - files: ['./*.[jt]s', './src/**/*.[jt]s'], - }), - new StyleLintPlugin({ - configFile: path.resolve(__dirname, '.stylelintrc.json'), - context: path.resolve(__dirname, './src'), - files: '**/*.css', - }), ], devServer: { watchFiles: ['src/**/*'],