From 66dae77fc52a53b33170f715d96f3e44482b24c3 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Mon, 17 May 2021 20:53:41 +0200 Subject: [PATCH] feat: use TypeScript in the client (#42124) --- .eslintrc.json | 32 ++- client/.babelrc | 3 +- client/package-lock.json | 25 ++- client/package.json | 2 + .../components/Flash/{index.js => index.tsx} | 24 ++- client/src/declarations.d.ts | 2 + ...culum-helpers.js => curriculum-helpers.ts} | 10 +- client/tsconfig.json | 16 ++ client/webpack-workers.js | 10 +- curriculum/package-lock.json | 31 +++ curriculum/package.json | 1 + curriculum/test/test-challenges.js | 2 +- jest.config.js | 2 +- package-lock.json | 195 ++++++++++++++++++ package.json | 9 +- 15 files changed, 332 insertions(+), 32 deletions(-) rename client/src/components/Flash/{index.js => index.tsx} (71%) create mode 100644 client/src/declarations.d.ts rename client/src/utils/{curriculum-helpers.js => curriculum-helpers.ts} (67%) create mode 100644 client/tsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json index 50eab4e668..8b14ec2661 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,11 +5,12 @@ "mocha": true, "node": true }, - "parser": "babel-eslint", + "parser": "@typescript-eslint/parser", "root": true, "extends": [ "./.eslintrc-base.json", - "prettier" + "plugin:prettier/recommended", + "plugin:import/typescript" ], "plugins": [ "react", @@ -31,13 +32,30 @@ "react": { "version": "16.4.2", "import/ignore": [ - "node_modules", "\\.json$" ], "import/extensions": [ ".js", - ".jsx" + ".jsx", + ".tsx" ] - } - } -} \ No newline at end of file + }, + "import/ignore": [ + "node_modules" + ] + }, + "overrides": [{ + "files": ["**/*.ts?(x)"], + "parserOptions": { + "tsconfigRootDir": "./client", + "project": ["./tsconfig.json"] + }, + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "plugins": [ + "@typescript-eslint" + ] + }] +} diff --git a/client/.babelrc b/client/.babelrc index a920248a91..1dee2091ee 100644 --- a/client/.babelrc +++ b/client/.babelrc @@ -19,7 +19,8 @@ "useBuiltIns": true, "pragma": "React.createElement" } - ] + ], + "@babel/preset-typescript" ], "plugins": [ "preval", diff --git a/client/package-lock.json b/client/package-lock.json index 9ae3c5770f..481f96045f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1719,13 +1719,19 @@ } }, "@babel/types": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", - "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", + "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==" + } } }, "@discoveryjs/json-ext": { @@ -3045,6 +3051,15 @@ } } }, + "@types/react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-vIo69qKKcYoJ8wKCJjwSgCTM+z3chw3g18dkrDfVX665tMH7tmbDxEAnPdey4gTlwZz5QuHGzd+hul0OVZDqqQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/readable-stream": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", diff --git a/client/package.json b/client/package.json index 6376c9fa21..e5de4c3c1b 100644 --- a/client/package.json +++ b/client/package.json @@ -122,8 +122,10 @@ "validator": "13.6.0" }, "devDependencies": { + "@babel/types": "^7.14.2", "@testing-library/jest-dom": "5.12.0", "@testing-library/react": "11.2.7", + "@types/react-transition-group": "^4.4.1", "autoprefixer": "10.2.5", "babel-plugin-transform-imports": "2.0.0", "chokidar": "3.5.1", diff --git a/client/src/components/Flash/index.js b/client/src/components/Flash/index.tsx similarity index 71% rename from client/src/components/Flash/index.js rename to client/src/components/Flash/index.tsx index 1336214704..01f7423541 100644 --- a/client/src/components/Flash/index.js +++ b/client/src/components/Flash/index.tsx @@ -6,18 +6,28 @@ import { useTranslation } from 'react-i18next'; import './flash.css'; -function Flash({ flashMessage, onClose }) { +type FlashProps = { + flashMessage: { + type: string; + message: string; + id: string; + variables: Record; + }; + onClose: () => void; +}; + +function Flash({ flashMessage, onClose }: FlashProps): JSX.Element { const { type, message, id, variables = {} } = flashMessage; const { t } = useTranslation(); - const [flashMessageHeight, setFlashMessageHeight] = useState(null); + const [flashMessageHeight, setFlashMessageHeight] = useState(0); useEffect(() => { - setFlashMessageHeight( - document.querySelector('.flash-message').offsetHeight - ); + const flashMessageElem: HTMLElement | null = + document.querySelector('.flash-message'); + setFlashMessageHeight(flashMessageElem?.offsetHeight || 0); document.documentElement.style.setProperty( '--flash-message-height', - flashMessageHeight + 'px' + `${flashMessageHeight}px` ); }, [flashMessageHeight]); @@ -42,7 +52,7 @@ function Flash({ flashMessage, onClose }) { {flashMessage && (
)} diff --git a/client/src/declarations.d.ts b/client/src/declarations.d.ts new file mode 100644 index 0000000000..a736520209 --- /dev/null +++ b/client/src/declarations.d.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/unambiguous +declare module '@freecodecamp/react-bootstrap'; diff --git a/client/src/utils/curriculum-helpers.js b/client/src/utils/curriculum-helpers.ts similarity index 67% rename from client/src/utils/curriculum-helpers.js rename to client/src/utils/curriculum-helpers.ts index 18d7015291..6bb55f6ae7 100644 --- a/client/src/utils/curriculum-helpers.js +++ b/client/src/utils/curriculum-helpers.ts @@ -1,11 +1,13 @@ import { parse } from '@babel/parser'; import generate from '@babel/generator'; -const removeHtmlComments = str => str.replace(/|$)/g, ''); +const removeHtmlComments = (str: string): string => + str.replace(/|$)/g, ''); -const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, ''); +const removeCssComments = (str: string): string => + str.replace(/\/\*[\s\S]+?\*\//g, ''); -export const removeJSComments = codeStr => { +export const removeJSComments = (codeStr: string): string => { // Note: removes trailing new lines and tailing spaces at end of lines const options = { comments: false, @@ -23,7 +25,7 @@ export const removeJSComments = codeStr => { } }; -const removeWhiteSpace = (str = '') => { +const removeWhiteSpace = (str = ''): string => { return str.replace(/\s/g, ''); }; diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000000..a1bf75d789 --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["./i18n/**/*", "./plugins/**/*","./src/**/*","./utils/**/*"], + "compilerOptions": { + "target": "es2020", + "module": "es2020", + "moduleResolution": "node", + "allowJs": true, + "jsx": "react", + "strict": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noEmit": true, + "skipLibCheck": true + } +} diff --git a/client/webpack-workers.js b/client/webpack-workers.js index 7bc5512fc5..735a635b09 100644 --- a/client/webpack-workers.js +++ b/client/webpack-workers.js @@ -37,8 +37,8 @@ module.exports = (env = {}) => { module: { rules: [ { - test: /\.jsx?$/, - include: [path.join(__dirname, 'src/client/')], + test: /\.(js|ts)$/, + exclude: /node_modules/, use: { loader: 'babel-loader', options: { @@ -47,7 +47,8 @@ module.exports = (env = {}) => { [ '@babel/preset-env', { modules: false, targets: '> 0.25%, not dead' } - ] + ], + '@babel/preset-typescript' ], plugins: [ '@babel/plugin-transform-runtime', @@ -75,7 +76,8 @@ module.exports = (env = {}) => { util: false, stream: false, process: require.resolve('process/browser') - } + }, + extensions: ['.js', '.ts'] } }; }; diff --git a/curriculum/package-lock.json b/curriculum/package-lock.json index ab1bbc66c4..2dbdd3a4aa 100644 --- a/curriculum/package-lock.json +++ b/curriculum/package-lock.json @@ -809,6 +809,15 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", + "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", @@ -1102,6 +1111,17 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-transform-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", + "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-typescript": "^7.12.13" + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", @@ -1249,6 +1269,17 @@ "esutils": "^2.0.2" } }, + "@babel/preset-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", + "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-transform-typescript": "^7.13.0" + } + }, "@babel/register": { "version": "7.13.16", "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.13.16.tgz", diff --git a/curriculum/package.json b/curriculum/package.json index 44c3d01a58..0a7625c691 100644 --- a/curriculum/package.json +++ b/curriculum/package.json @@ -33,6 +33,7 @@ "@babel/core": "7.14.2", "@babel/polyfill": "7.12.1", "@babel/preset-env": "7.14.2", + "@babel/preset-typescript": "^7.13.0", "@babel/register": "7.13.16", "acorn": "8.2.4", "acorn-jsx": "5.3.1", diff --git a/curriculum/test/test-challenges.js b/curriculum/test/test-challenges.js index 1ce850fc98..2ca81404b3 100644 --- a/curriculum/test/test-challenges.js +++ b/curriculum/test/test-challenges.js @@ -11,7 +11,7 @@ require('@babel/polyfill'); require('@babel/register')({ root: clientPath, babelrc: false, - presets: ['@babel/preset-env'], + presets: ['@babel/preset-env', '@babel/typescript'], plugins: ['dynamic-import-node'], ignore: [/node_modules/], only: [clientPath] diff --git a/jest.config.js b/jest.config.js index 6538ca8db3..ed27610807 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,7 +18,7 @@ module.exports = { }, verbose: true, transform: { - '^.+\\.js$': '/jest.transform.js' + '^.+\\.[jt]sx?$': '/jest.transform.js' }, roots: ['.', './client', './api-server'], transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'], diff --git a/package-lock.json b/package-lock.json index e3f4a8c982..07e3fa3871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -724,6 +724,15 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.13.tgz", + "integrity": "sha512-cHP3u1JiUiG2LFDKbXnwVad81GvfyIOmCD6HIEId6ojrY0Drfy2q1jw7BwN7dE84+kTnBjLkXoL3IEy/3JPu2w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz", @@ -1174,6 +1183,17 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-transform-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.13.0.tgz", + "integrity": "sha512-elQEwluzaU8R8dbVuW2Q2Y8Nznf7hnjM7+DSCd14Lo5fF63C9qNLbwZYbmZrtV9/ySpSUpkRpQXvJb6xyu4hCQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.13.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-typescript": "^7.12.13" + } + }, "@babel/plugin-transform-unicode-escapes": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz", @@ -1325,6 +1345,17 @@ "@babel/plugin-transform-react-pure-annotations": "^7.12.1" } }, + "@babel/preset-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.13.0.tgz", + "integrity": "sha512-LXJwxrHy0N3f6gIJlYbLta1D9BDtHpQeqwzM0LIfjDlr6UE/D5Mc7W4iDiQzaE+ks0sTjT26ArcHWnJVt0QiHw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/helper-validator-option": "^7.12.17", + "@babel/plugin-transform-typescript": "^7.13.0" + } + }, "@babel/runtime": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz", @@ -5467,6 +5498,12 @@ "pretty-format": "^26.0.0" } }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -5551,6 +5588,149 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz", + "integrity": "sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.23.0", + "@typescript-eslint/scope-manager": "4.23.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz", + "integrity": "sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.23.0", + "@typescript-eslint/types": "4.23.0", + "@typescript-eslint/typescript-estree": "4.23.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.23.0.tgz", + "integrity": "sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.23.0", + "@typescript-eslint/types": "4.23.0", + "@typescript-eslint/typescript-estree": "4.23.0", + "debug": "^4.1.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz", + "integrity": "sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.23.0", + "@typescript-eslint/visitor-keys": "4.23.0" + } + }, + "@typescript-eslint/types": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.23.0.tgz", + "integrity": "sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz", + "integrity": "sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.23.0", + "@typescript-eslint/visitor-keys": "4.23.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz", + "integrity": "sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.23.0", + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -20534,6 +20714,15 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -20592,6 +20781,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", diff --git a/package.json b/package.json index b518605da9..a72874768f 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "lint:challenges": "cd ./curriculum && npm run lint", "lint:css": "npm run prettier -- --check", "lint:js": "eslint .", + "lint:ts": "tsc -p client", "lint:translations": "cd ./client && npm run lint", "postinstall": "npm run bootstrap", "prettier": "prettier \"**/*.css\"", @@ -88,7 +89,7 @@ }, "lint-staged": { "*.css": "prettier --write", - "*.js": "eslint --cache --fix", + "*.(js|ts|tsx)": "eslint --cache --fix", "./curriculum/challenges/**/*.md": "node ./tools/scripts/lint/index.js" }, "dependencies": { @@ -99,14 +100,17 @@ "@babel/plugin-proposal-function-bind": "7.12.13", "@babel/preset-env": "7.14.2", "@babel/preset-react": "7.13.13", + "@babel/preset-typescript": "^7.13.0", "@testing-library/cypress": "7.0.6", "@testing-library/jest-dom": "5.12.0", + "@typescript-eslint/eslint-plugin": "^4.23.0", + "@typescript-eslint/parser": "^4.23.0", "babel-eslint": "10.1.0", "babel-plugin-transform-imports": "2.0.0", "cross-env": "7.0.3", "cypress": "7.3.0", "docsify-cli": "4.4.3", - "eslint": "7.26.0", + "eslint": "^7.26.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-import": "2.23.2", "eslint-plugin-jsx-a11y": "6.4.1", @@ -130,6 +134,7 @@ "sinon": "10.0.0", "sinon-express-mock": "2.2.1", "start-server-and-test": "1.12.2", + "typescript": "^4.2.4", "webpack-bundle-analyzer": "4.4.2" } }