From d74e547682cfc45d36cd3b033e011d5a2dda0ff4 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Mon, 14 Dec 2020 14:11:14 -0500 Subject: [PATCH 1/5] Add TypeScript Configuration. --- package-lock.json | 260 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 7 +- tsconfig.json | 71 +++++++++++++ webpack.config.js | 38 +++---- 4 files changed, 356 insertions(+), 20 deletions(-) create mode 100644 tsconfig.json diff --git a/package-lock.json b/package-lock.json index 689842b..0a18e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "github-battle", + "name": "github-battle-hooks", "version": "1.0.0", "lockfileVersion": 1, "requires": true, @@ -252,6 +252,18 @@ "@babel/types": "^7.4.4" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz", + "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==", + "dev": true + }, "@babel/helper-wrap-function": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", @@ -418,6 +430,23 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + } + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", @@ -755,6 +784,179 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-transform-typescript": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-typescript": "^7.12.1" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/generator": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.10.tgz", + "integrity": "sha512-6mCdfhWgmqLdtTkhXjnIz0LcdVCd26wS2JXRtj2XY0u5klDsXBREA/pG5NVOuVnF2LUrBGNFtQkIqqTbblg0ww==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "dev": true, + "requires": { + "@babel/types": "^7.12.7" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.10.tgz", + "integrity": "sha512-PJdRPwyoOqFAWfLytxrWwGrAxghCgh/yTNCYciOz8QgjflA7aZhECPZAa2VUedKg2+QMWkI0L9lynh2SNmNEgA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", + "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.10", + "@babel/types": "^7.12.10", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.10.tgz", + "integrity": "sha512-sf6wboJV5mGyip2hIpDSKsr80RszPinEFjsHTalMxZAZkoQ2/2yQzxlcFN52SJqsyPfLtPmenL4g2KB3KJXPDw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + } + } + }, "@babel/plugin-transform-unicode-regex": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz", @@ -837,6 +1039,25 @@ "@babel/plugin-transform-react-jsx-source": "^7.0.0" } }, + "@babel/preset-typescript": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz", + "integrity": "sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-transform-typescript": "^7.12.1" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + } + } + }, "@babel/runtime": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", @@ -913,6 +1134,31 @@ "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", "dev": true }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -2206,6 +2452,12 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + }, "cyclist": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", @@ -6858,6 +7110,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "dev": true + }, "uglify-js": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", diff --git a/package.json b/package.json index 7f9e7c3..fa051f2 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "babel": { "presets": [ "@babel/preset-env", - "@babel/preset-react" + "@babel/preset-react", + "@babel/preset-typescript" ], "plugins": [ "@babel/plugin-proposal-class-properties", @@ -34,12 +35,16 @@ "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/preset-env": "^7.5.5", "@babel/preset-react": "^7.0.0", + "@babel/preset-typescript": "^7.12.7", + "@types/react": "^17.0.0", + "@types/react-dom": "^17.0.0", "babel-loader": "^8.0.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", "copy-webpack-plugin": "^5.0.3", "css-loader": "^3.1.0", "html-webpack-plugin": "^3.2.0", "style-loader": "^0.23.1", + "typescript": "^4.1.3", "webpack": "^4.36.1", "webpack-cli": "^3.3.6", "webpack-dev-server": "^3.7.2" diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff47636 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,71 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": ["DOM","ESNext"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": ["app/**/*"] +} diff --git a/webpack.config.js b/webpack.config.js index b376c52..7aa6055 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,30 +1,32 @@ -const path = require('path') -const HtmlWebpackPlugin = require('html-webpack-plugin') -const CopyPlugin = require('copy-webpack-plugin') +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const CopyPlugin = require("copy-webpack-plugin"); module.exports = { - entry: './app/index.js', + entry: "./app/index.tsx", output: { - path: path.resolve(__dirname, 'dist'), - filename: 'index_bundle.js', - publicPath: '/' + path: path.resolve(__dirname, "dist"), + filename: "index_bundle.js", + publicPath: "/", + }, + resolve: { + // Add `.ts` and `.tsx` as a resolvable extension. + extensions: [".ts", ".tsx", ".js"], }, module: { rules: [ - { test: /\.(js)$/, use: 'babel-loader' }, - { test: /\.css$/, use: [ 'style-loader', 'css-loader' ]} - ] + { test: /\.(js|ts|tsx)$/, use: "babel-loader" }, + { test: /\.css$/, use: ["style-loader", "css-loader"] }, + ], }, - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', + mode: process.env.NODE_ENV === "production" ? "production" : "development", plugins: [ new HtmlWebpackPlugin({ - template: 'app/index.html' + template: "app/index.html", }), - new CopyPlugin([ - { from : '_redirects' } - ]) + new CopyPlugin([{ from: "_redirects" }]), ], devServer: { - historyApiFallback: true - } -} \ No newline at end of file + historyApiFallback: true, + }, +}; From 131c4ea98c255bcfe5b6256ceb6c18e5bbdb0070 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Mon, 14 Dec 2020 14:11:58 -0500 Subject: [PATCH 2/5] Convert the API file to TypeScript --- app/utils/api.js | 78 -------------------------------------- app/utils/api.ts | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 78 deletions(-) delete mode 100644 app/utils/api.js create mode 100644 app/utils/api.ts diff --git a/app/utils/api.js b/app/utils/api.js deleted file mode 100644 index 080865b..0000000 --- a/app/utils/api.js +++ /dev/null @@ -1,78 +0,0 @@ -const id = "YOUR_CLIENT_ID" -const sec = "YOUR_SECRET_ID" -const params = `?client_id=${id}&client_secret=${sec}` - -function getErrorMsg (message, username) { - if (message === 'Not Found') { - return `${username} doesn't exist` - } - - return message -} - -function getProfile (username) { - return fetch(`https://api.github.com/users/${username}${params}`) - .then((res) => res.json()) - .then((profile) => { - if (profile.message) { - throw new Error(getErrorMsg(profile.message, username)) - } - - return profile - }) -} - -function getRepos (username) { - return fetch(`https://api.github.com/users/${username}/repos${params}&per_page=100`) - .then((res) => res.json()) - .then((repos) => { - if (repos.message) { - throw new Error(getErrorMsg(repos.message, username)) - } - - return repos - }) -} - -function getStarCount (repos) { - return repos.reduce((count, { stargazers_count }) => count + stargazers_count , 0) -} - -function calculateScore (followers, repos) { - return (followers * 3) + getStarCount(repos) -} - -function getUserData (player) { - return Promise.all([ - getProfile(player), - getRepos(player) - ]).then(([ profile, repos ]) => ({ - profile, - score: calculateScore(profile.followers, repos) - })) -} - -function sortPlayers (players) { - return players.sort((a, b) => b.score - a.score) -} - -export function battle (players) { - return Promise.all([ - getUserData(players[0]), - getUserData(players[1]) - ]).then((results) => sortPlayers(results)) -} - -export function fetchPopularRepos (language) { - const endpoint = window.encodeURI(`https://api.github.com/search/repositories?q=stars:>1+language:${language}&sort=stars&order=desc&type=Repositories`) - - return fetch(endpoint) - .then((res) => res.json()) - .then((data) => { - if (!data.items) { - throw new Error(data.message) - } - - return data.items - }) -} \ No newline at end of file diff --git a/app/utils/api.ts b/app/utils/api.ts new file mode 100644 index 0000000..8f4ec77 --- /dev/null +++ b/app/utils/api.ts @@ -0,0 +1,97 @@ +const id = "YOUR_CLIENT_ID"; +const sec = "YOUR_SECRET_ID"; +const params = `?client_id=${id}&client_secret=${sec}`; + +function getErrorMsg(message: string, username: string) { + if (message === "Not Found") { + return `${username} doesn't exist`; + } + + return message; +} + +export interface User { + id: string; + followers: number; +} +function getProfile(username: string): Promise { + return fetch(`https://api.github.com/users/${username}${params}`) + .then((res) => res.json()) + .then((profile) => { + if (profile.message) { + throw new Error(getErrorMsg(profile.message, username)); + } + + return profile; + }); +} + +export interface Repo { + id: string; + stargazers_count: number; +} + +function getRepos(username: string): Promise { + return fetch( + `https://api.github.com/users/${username}/repos${params}&per_page=100` + ) + .then((res) => res.json()) + .then((repos) => { + if (repos.message) { + throw new Error(getErrorMsg(repos.message, username)); + } + + return repos; + }); +} + +function getStarCount(repos: Repo[]) { + return repos.reduce( + (count, { stargazers_count }) => count + stargazers_count, + 0 + ); +} + +function calculateScore(followers: number, repos: Repo[]) { + return followers * 3 + getStarCount(repos); +} + +export interface Player { + profile: User; + score: number; +} +function getUserData(player: string): Promise { + return Promise.all([getProfile(player), getRepos(player)]).then( + ([profile, repos]) => ({ + profile, + score: calculateScore(profile.followers, repos), + }) + ); +} + +function sortPlayers(players: [Player, Player]) { + return players.sort((a, b) => b.score - a.score); +} + +export function battle(players: [string, string]) { + return Promise.all([ + getUserData(players[0]), + getUserData(players[1]), + ]).then((results) => sortPlayers(results)); +} + +export function fetchPopularRepos(language: string): Promise { + const endpoint = window.encodeURI( + `https://api.github.com/search/repositories?q=stars:>1+language:${language}&sort=stars&order=desc&type=Repositories` + ); + + return fetch(endpoint) + .then((res) => res.json()) + .then((data) => { + if (!data.items) { + throw new Error(data.message); + } + + return data.items; + }); +} From e64a42d0140bb2bc7ce72d9ca727173ad6c04553 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Mon, 14 Dec 2020 14:18:46 -0500 Subject: [PATCH 3/5] Convert useHover, Tooltip, and theme context to TypeScript --- app/components/Tooltip.js | 40 ---------------------- app/components/Tooltip.tsx | 46 ++++++++++++++++++++++++++ app/contexts/theme.js | 7 ---- app/contexts/theme.ts | 7 ++++ app/hooks/{useHover.js => useHover.ts} | 0 5 files changed, 53 insertions(+), 47 deletions(-) delete mode 100644 app/components/Tooltip.js create mode 100644 app/components/Tooltip.tsx delete mode 100644 app/contexts/theme.js create mode 100644 app/contexts/theme.ts rename app/hooks/{useHover.js => useHover.ts} (100%) diff --git a/app/components/Tooltip.js b/app/components/Tooltip.js deleted file mode 100644 index 09fce1e..0000000 --- a/app/components/Tooltip.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import useHover from '../hooks/useHover' - -const styles = { - container: { - position: 'relative', - display: 'flex' - }, - tooltip: { - boxSizing: 'border-box', - position: 'absolute', - width: '160px', - bottom: '100%', - left: '50%', - marginLeft: '-80px', - borderRadius: '3px', - backgroundColor: 'hsla(0, 0%, 20%, 0.9)', - padding: '7px', - marginBottom: '5px', - color: '#fff', - textAlign: 'center', - fontSize: '14px', - } -} - -export default function Tooltip ({ text, children }) { - const [hovering, attrs] = useHover() - - return ( -
- {hovering === true &&
{text}
} - {children} -
- ) -} - -Tooltip.propTypes = { - text: PropTypes.string.isRequired, -} \ No newline at end of file diff --git a/app/components/Tooltip.tsx b/app/components/Tooltip.tsx new file mode 100644 index 0000000..10523f7 --- /dev/null +++ b/app/components/Tooltip.tsx @@ -0,0 +1,46 @@ +import React, { CSSProperties, ReactNode } from "react"; +import PropTypes from "prop-types"; +import useHover from "../hooks/useHover"; + +const styles = { + container: { + position: "relative", + display: "flex", + } as CSSProperties, + tooltip: { + boxSizing: "border-box", + position: "absolute", + width: "160px", + bottom: "100%", + left: "50%", + marginLeft: "-80px", + borderRadius: "3px", + backgroundColor: "hsla(0, 0%, 20%, 0.9)", + padding: "7px", + marginBottom: "5px", + color: "#fff", + textAlign: "center", + fontSize: "14px", + } as CSSProperties, +}; + +export default function Tooltip({ + text, + children, +}: { + text: ReactNode; + children: ReactNode; +}) { + const [hovering, attrs] = useHover(); + + return ( +
+ {hovering === true &&
{text}
} + {children} +
+ ); +} + +Tooltip.propTypes = { + text: PropTypes.string.isRequired, +}; diff --git a/app/contexts/theme.js b/app/contexts/theme.js deleted file mode 100644 index 9373368..0000000 --- a/app/contexts/theme.js +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react' - -const ThemeContext = React.createContext() - -export default ThemeContext -export const ThemeConsumer = ThemeContext.Consumer -export const ThemeProvider = ThemeContext.Provider diff --git a/app/contexts/theme.ts b/app/contexts/theme.ts new file mode 100644 index 0000000..5f3a2b9 --- /dev/null +++ b/app/contexts/theme.ts @@ -0,0 +1,7 @@ +import React from "react"; + +const ThemeContext = React.createContext("light"); + +export default ThemeContext; +export const ThemeConsumer = ThemeContext.Consumer; +export const ThemeProvider = ThemeContext.Provider; diff --git a/app/hooks/useHover.js b/app/hooks/useHover.ts similarity index 100% rename from app/hooks/useHover.js rename to app/hooks/useHover.ts From aaeb03c89805405fd1631e2605b087f048672e31 Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Mon, 14 Dec 2020 14:20:28 -0500 Subject: [PATCH 4/5] Convert the Battle component to TypeScript --- app/components/Battle.js | 170 --------------------------------- app/components/Battle.tsx | 193 ++++++++++++++++++++++++++++++++++++++ package-lock.json | 27 ++++++ package.json | 1 + 4 files changed, 221 insertions(+), 170 deletions(-) delete mode 100644 app/components/Battle.js create mode 100644 app/components/Battle.tsx diff --git a/app/components/Battle.js b/app/components/Battle.js deleted file mode 100644 index 6576e8f..0000000 --- a/app/components/Battle.js +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react' -import { FaUserFriends, FaFighterJet, FaTrophy, FaTimesCircle } from 'react-icons/fa' -import PropTypes from 'prop-types' -import Results from './Results' -import ThemeContext from '../contexts/theme' -import { Link } from 'react-router-dom' - -function Instructions () { - const theme = React.useContext(ThemeContext) - - return ( -
-

- Instructions -

-
    -
  1. -

    Enter two Github users

    - -
  2. -
  3. -

    Battle

    - -
  4. -
  5. -

    See the winners

    - -
  6. -
-
- ) -} - -function PlayerInput ({ onSubmit, label }) { - const [username, setUsername] = React.useState('') - - const handleSubmit = (e) => { - e.preventDefault() - - onSubmit(username) - } - - const handleChange = (event) => setUsername(event.target.value) - const theme = React.useContext(ThemeContext) - - return ( -
- -
- - -
-
- ) -} - -PlayerInput.propTypes = { - onSubmit: PropTypes.func.isRequired, - label: PropTypes.string.isRequired -} - -function PlayerPreview ({ username, onReset, label }) { - const theme = React.useContext(ThemeContext) - - return ( -
-

{label}

-
- - -
-
- ) -} - -PlayerPreview.propTypes = { - username: PropTypes.string.isRequired, - onReset: PropTypes.func.isRequired, - label: PropTypes.string.isRequired -} - -export default function Battle () { - const [playerOne, setPlayerOne] = React.useState(null) - const [playerTwo, setPlayerTwo] = React.useState(null) - - const handleSubmit = (id, player) => id === 'playerOne' - ? setPlayerOne(player) - : setPlayerTwo(player) - - const handleReset = (id) => id === 'playerOne' - ? setPlayerOne(null) - : setPlayerTwo(null) - - return ( - - - -
-

Players

-
- {playerOne === null - ? handleSubmit('playerOne', player)} - /> - : handleReset('playerOne')} - /> - } - - {playerTwo === null - ? handleSubmit('playerTwo', player)} - /> - : handleReset('playerTwo')} - /> - } -
- - - {playerOne && playerTwo && ( - - Battle - - )} -
-
- ) -} \ No newline at end of file diff --git a/app/components/Battle.tsx b/app/components/Battle.tsx new file mode 100644 index 0000000..3cdef93 --- /dev/null +++ b/app/components/Battle.tsx @@ -0,0 +1,193 @@ +import React, { ChangeEvent, FormEvent } from "react"; +import { + FaUserFriends, + FaFighterJet, + FaTrophy, + FaTimesCircle, +} from "react-icons/fa"; +import PropTypes from "prop-types"; +import Results from "./Results"; +import ThemeContext from "../contexts/theme"; +import { Link } from "react-router-dom"; + +function Instructions() { + const theme = React.useContext(ThemeContext); + + return ( +
+

Instructions

+
    +
  1. +

    Enter two Github users

    + +
  2. +
  3. +

    Battle

    + +
  4. +
  5. +

    See the winners

    + +
  6. +
+
+ ); +} + +function PlayerInput({ + onSubmit, + label, +}: { + onSubmit: (username: string) => void; + label: string; +}) { + const [username, setUsername] = React.useState(""); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + + onSubmit(username); + }; + + const handleChange = (event: ChangeEvent) => + setUsername(event.target.value); + const theme = React.useContext(ThemeContext); + + return ( +
+ +
+ + +
+
+ ); +} + +PlayerInput.propTypes = { + onSubmit: PropTypes.func.isRequired, + label: PropTypes.string.isRequired, +}; + +function PlayerPreview({ + username, + onReset, + label, +}: { + label: string; + username: string; + onReset: () => void; +}) { + const theme = React.useContext(ThemeContext); + + return ( +
+

{label}

+
+ + +
+
+ ); +} + +PlayerPreview.propTypes = { + username: PropTypes.string.isRequired, + onReset: PropTypes.func.isRequired, + label: PropTypes.string.isRequired, +}; + +export default function Battle() { + const [playerOne, setPlayerOne] = React.useState(null); + const [playerTwo, setPlayerTwo] = React.useState(null); + + const handleSubmit = (id: string, player: string) => + id === "playerOne" ? setPlayerOne(player) : setPlayerTwo(player); + + const handleReset = (id: string) => + id === "playerOne" ? setPlayerOne(null) : setPlayerTwo(null); + + return ( + + + +
+

Players

+
+ {playerOne === null ? ( + handleSubmit("playerOne", player)} + /> + ) : ( + handleReset("playerOne")} + /> + )} + + {playerTwo === null ? ( + handleSubmit("playerTwo", player)} + /> + ) : ( + handleReset("playerTwo")} + /> + )} +
+ + {playerOne && playerTwo && ( + + Battle + + )} +
+
+ ); +} diff --git a/package-lock.json b/package-lock.json index 0a18e2a..16b1066 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1122,6 +1122,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -1159,6 +1165,27 @@ "@types/react": "*" } }, + "@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.6.tgz", + "integrity": "sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g==", + "dev": true, + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", diff --git a/package.json b/package.json index fa051f2..76ecaac 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@babel/preset-typescript": "^7.12.7", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", + "@types/react-router-dom": "^5.1.6", "babel-loader": "^8.0.6", "babel-plugin-syntax-dynamic-import": "^6.18.0", "copy-webpack-plugin": "^5.0.3", From d49d908b90d9bd020c8591d24414e7ad6f9e352a Mon Sep 17 00:00:00 2001 From: Alex Anderson Date: Mon, 14 Dec 2020 14:21:22 -0500 Subject: [PATCH 5/5] Conver the Card, Loading, and Nav components to TypeScript. --- app/components/Card.js | 39 ---------------------------------- app/components/Card.tsx | 43 ++++++++++++++++++++++++++++++++++++++ app/components/Loading.js | 40 ----------------------------------- app/components/Loading.tsx | 34 ++++++++++++++++++++++++++++++ app/components/Nav.js | 42 ------------------------------------- app/components/Nav.tsx | 35 +++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 121 deletions(-) delete mode 100644 app/components/Card.js create mode 100644 app/components/Card.tsx delete mode 100644 app/components/Loading.js create mode 100644 app/components/Loading.tsx delete mode 100644 app/components/Nav.js create mode 100644 app/components/Nav.tsx diff --git a/app/components/Card.js b/app/components/Card.js deleted file mode 100644 index d719e5a..0000000 --- a/app/components/Card.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import ThemeContext from '../contexts/theme' - -export default function Card ({ header, subheader, avatar, href, name, children }) { - const theme = React.useContext(ThemeContext) - - return ( -
-

- {header} -

- {`Avatar - {subheader && ( -

- {subheader} -

- )} -

- - {name} - -

- {children} -
- ) -} - -Card.propTypes = { - header: PropTypes.string.isRequired, - subheader: PropTypes.string, - avatar: PropTypes.string.isRequired, - href: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, -} \ No newline at end of file diff --git a/app/components/Card.tsx b/app/components/Card.tsx new file mode 100644 index 0000000..e3e84dc --- /dev/null +++ b/app/components/Card.tsx @@ -0,0 +1,43 @@ +import React, { ReactNode } from "react"; +import PropTypes from "prop-types"; +import ThemeContext from "../contexts/theme"; + +export default function Card({ + header, + subheader, + avatar, + href, + name, + children, +}: { + header: string; + subheader?: string; + avatar: string; + href: string; + name: string; + children: ReactNode; +}) { + const theme = React.useContext(ThemeContext); + + return ( +
+

{header}

+ {`Avatar + {subheader &&

{subheader}

} +

+ + {name} + +

+ {children} +
+ ); +} + +Card.propTypes = { + header: PropTypes.string.isRequired, + subheader: PropTypes.string, + avatar: PropTypes.string.isRequired, + href: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, +}; diff --git a/app/components/Loading.js b/app/components/Loading.js deleted file mode 100644 index 99f1aee..0000000 --- a/app/components/Loading.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -const styles = { - content: { - fontSize: '35px', - position: 'absolute', - left: '0', - right: '0', - marginTop: '20px', - textAlign: 'center', - } -} - -export default function Loading ({ text = 'Loading', speed = 300 }) { - const [content, setContent] = React.useState(text) - - React.useEffect(() => { - const id = window.setInterval(() => { - setContent((content) => { - return content === `${text}...` - ? text - : `${content}.` - }) - }, speed) - - return () => window.clearInterval(id) - }, [text, speed]) - - return ( -

- {content} -

- ) -} - -Loading.propTypes = { - text: PropTypes.string, - speed: PropTypes.number, -} \ No newline at end of file diff --git a/app/components/Loading.tsx b/app/components/Loading.tsx new file mode 100644 index 0000000..5183ff3 --- /dev/null +++ b/app/components/Loading.tsx @@ -0,0 +1,34 @@ +import React, { CSSProperties } from "react"; +import PropTypes from "prop-types"; + +const styles = { + content: { + fontSize: "35px", + position: "absolute", + left: "0", + right: "0", + marginTop: "20px", + textAlign: "center", + } as CSSProperties, +}; + +export default function Loading({ text = "Loading", speed = 300 }) { + const [content, setContent] = React.useState(text); + + React.useEffect(() => { + const id = window.setInterval(() => { + setContent((content) => { + return content === `${text}...` ? text : `${content}.`; + }); + }, speed); + + return () => window.clearInterval(id); + }, [text, speed]); + + return

{content}

; +} + +Loading.propTypes = { + text: PropTypes.string, + speed: PropTypes.number, +}; diff --git a/app/components/Nav.js b/app/components/Nav.js deleted file mode 100644 index 90b0cab..0000000 --- a/app/components/Nav.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import ThemeContext from '../contexts/theme' -import { NavLink } from 'react-router-dom' - -const activeStyle = { - color: 'rgb(187, 46, 31)' -} - -export default function Nav ({ toggleTheme }) { - const theme = React.useContext(ThemeContext) - - return ( - - ) -} \ No newline at end of file diff --git a/app/components/Nav.tsx b/app/components/Nav.tsx new file mode 100644 index 0000000..ac90c2a --- /dev/null +++ b/app/components/Nav.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import ThemeContext from "../contexts/theme"; +import { NavLink } from "react-router-dom"; + +const activeStyle = { + color: "rgb(187, 46, 31)", +}; + +export default function Nav({ toggleTheme }: { toggleTheme: () => void }) { + const theme = React.useContext(ThemeContext); + + return ( + + ); +}