90 | )
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/frontend/components/AppFunctional.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | // Suggested initial states
4 | const initialMessage = ''
5 | const initialEmail = ''
6 | const initialSteps = 0
7 | const initialIndex = 4 // the index the "B" is at
8 |
9 | export default function AppFunctional(props) {
10 | // THE FOLLOWING HELPERS ARE JUST RECOMMENDATIONS.
11 | // You can delete them and build your own logic from scratch.
12 |
13 | function getXY() {
14 | // It it not necessary to have a state to track the coordinates.
15 | // It's enough to know what index the "B" is at, to be able to calculate them.
16 | }
17 |
18 | function getXYMessage() {
19 | // It it not necessary to have a state to track the "Coordinates (2, 2)" message for the user.
20 | // You can use the `getXY` helper above to obtain the coordinates, and then `getXYMessage`
21 | // returns the fully constructed string.
22 | }
23 |
24 | function reset() {
25 | // Use this helper to reset all states to their initial values.
26 | }
27 |
28 | function getNextIndex(direction) {
29 | // This helper takes a direction ("left", "up", etc) and calculates what the next index
30 | // of the "B" would be. If the move is impossible because we are at the edge of the grid,
31 | // this helper should return the current index unchanged.
32 | }
33 |
34 | function move(evt) {
35 | // This event handler can use the helper above to obtain a new index for the "B",
36 | // and change any states accordingly.
37 | }
38 |
39 | function onChange(evt) {
40 | // You will need this to update the value of the input.
41 | }
42 |
43 | function onSubmit(evt) {
44 | // Use a POST request to send a payload to the server.
45 | }
46 |
47 | return (
48 |
77 | )
78 | }
79 |
--------------------------------------------------------------------------------
/frontend/fonts/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students of MA course of Visual design. Some rights reserved.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/frontend/fonts/TitilliumWeb-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bloominstituteoftechnology/web-sprint-challenge-advanced-react/3b22e6f6c0b76bcafc783f0de1dd8c6ab23f2d1b/frontend/fonts/TitilliumWeb-SemiBold.ttf
--------------------------------------------------------------------------------
/frontend/images/Light-Gradient.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bloominstituteoftechnology/web-sprint-challenge-advanced-react/3b22e6f6c0b76bcafc783f0de1dd8c6ab23f2d1b/frontend/images/Light-Gradient.jpeg
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 | Grid
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/frontend/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import { BrowserRouter, NavLink, Routes, Route } from 'react-router-dom'
4 | import AppClass from './components/AppClass'
5 | import AppFunctional from './components/AppFunctional'
6 | import './styles/reset.css'
7 | import './styles/styles.css'
8 |
9 | const container = document.getElementById('root')
10 | const root = createRoot(container)
11 |
12 | root.render(
13 |
14 |
Welcome to the GRID
15 |
19 |
20 | } />
21 | } />
22 |
23 |
24 | )
25 |
--------------------------------------------------------------------------------
/frontend/styles/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html, body, div, span, applet, object, iframe,
7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8 | a, abbr, acronym, address, big, cite, code,
9 | del, dfn, em, img, ins, kbd, q, s, samp,
10 | small, strike, strong, sub, sup, tt, var,
11 | b, u, i, center,
12 | dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend,
14 | table, caption, tbody, tfoot, thead, tr, th, td,
15 | article, aside, canvas, details, embed,
16 | figure, figcaption, footer, header, hgroup,
17 | menu, nav, output, ruby, section, summary,
18 | time, mark, audio, video {
19 | margin: 0;
20 | padding: 0;
21 | border: 0;
22 | font-size: 100%;
23 | font: inherit;
24 | vertical-align: baseline;
25 | }
26 |
27 | /* HTML5 display-role reset for older browsers */
28 | article, aside, details, figcaption, figure,
29 | footer, header, hgroup, menu, nav, section {
30 | display: block;
31 | }
32 |
33 | body {
34 | line-height: 1;
35 | }
36 |
37 | ol, ul {
38 | list-style: none;
39 | }
40 |
41 | blockquote, q {
42 | quotes: none;
43 | }
44 |
45 | blockquote:before, blockquote:after,
46 | q:before, q:after {
47 | content: '';
48 | content: none;
49 | }
50 |
51 | table {
52 | border-collapse: collapse;
53 | border-spacing: 0;
54 | }
55 |
56 | html {
57 | box-sizing: border-box;
58 | }
59 |
60 | *, *:before, *:after {
61 | box-sizing: inherit;
62 | }
63 |
--------------------------------------------------------------------------------
/frontend/styles/styles.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Titillium Web';
3 | src: url('../fonts/TitilliumWeb-SemiBold.ttf');
4 | }
5 |
6 | html, body {
7 | min-height:100vh;
8 | }
9 |
10 | body::after {
11 | content: "";
12 | background: url('../images/Light-Gradient.jpeg');
13 | opacity: 0.3;
14 | top: 0;
15 | left: 0;
16 | bottom: 0;
17 | right: 0;
18 | position: absolute;
19 | z-index: -1;
20 | }
21 |
22 | body, input, button {
23 | font-family: 'Titillium Web', sans-serif;
24 | color: #484848;
25 | }
26 |
27 | #root, #wrapper {
28 | display: flex;
29 | flex-direction: column;
30 | align-items: center;
31 | }
32 |
33 | h1 {
34 | font-size: 1.8rem;
35 | line-height: 4rem;
36 | color: #93005a;
37 | }
38 |
39 | nav a {
40 | line-height: 3.2rem;
41 | padding-left: 1rem;
42 | padding-right: 1rem;
43 | color: #484848;
44 | }
45 |
46 | nav a.active:nth-last-of-type(1) {
47 | color: #00808c;
48 | }
49 |
50 | nav a.active:nth-last-of-type(2) {
51 | color: #ff4b00;
52 | }
53 |
54 | .info {
55 | width: 100%;
56 | display: flex;
57 | flex-direction: column;
58 | align-items: center;
59 | justify-content: space-evenly;
60 | min-height: 3.8rem;
61 | background-color: rgb(252, 252, 252);
62 | border: 1px solid rgb(238, 238, 238);
63 | margin: 1rem 0 1rem 0;
64 | }
65 |
66 | #coordinates, #steps {
67 | font-size: 1.2rem;
68 | }
69 |
70 | #message {
71 | font-size: 1.2rem;
72 | color: #00808c;
73 | font-style: italic;
74 | }
75 |
76 | .class-based #message {
77 | color: #ff4b00;
78 | }
79 |
80 | #grid {
81 | display: grid;
82 | grid-template-columns: 100px 100px 100px;
83 | grid-template-rows: 100px 100px 100px;
84 | }
85 |
86 | #grid .square {
87 | border: 4px solid black;
88 | margin: -2px;
89 | display: flex;
90 | align-items: center;
91 | justify-content: center;
92 | font-size: 4rem;
93 | color: white;
94 | }
95 |
96 | .class-based #grid .square, .functional #grid .square.active {
97 | background-color: #00808c
98 | }
99 |
100 | .class-based #grid .square.active, .functional #grid .square {
101 | background-color: #ff4b00;
102 | }
103 |
104 | #keypad {
105 | background-color: rgb(252, 252, 252);
106 | border-radius: 50%;
107 | border: 1px solid rgb(238, 238, 238);
108 | padding: 0.55rem;
109 | margin-bottom: 1rem;
110 | display: grid;
111 | grid-template-columns: 55px 55px 55px;
112 | grid-template-rows: 55px 55px 55px;
113 | grid-template-areas:
114 | ". up ."
115 | "left reset right"
116 | ". down .";
117 | }
118 |
119 | #keypad button {
120 | border: 1px solid #DDDDDD;
121 | border-radius: 28%;
122 | align-self: center;
123 | justify-self: center;
124 | width: 55px;
125 | height: 55px;
126 | }
127 |
128 | #keypad button:active {
129 | box-shadow: 0 0 5px #93005a;
130 | border: 1px solid #93005a;
131 | background-color: white;
132 | }
133 |
134 | button#reset {
135 | width: 80%;
136 | height: 80%;
137 | border-radius: 50%;
138 | grid-area: reset;
139 | }
140 |
141 | button#left {
142 | grid-area: left;
143 | }
144 |
145 | button#up {
146 | grid-area: up;
147 | }
148 |
149 | button#right {
150 | grid-area: right;
151 | }
152 |
153 | button#down {
154 | grid-area: down;
155 | }
156 |
157 | input {
158 | height: 1.9rem;
159 | border-radius: 0;
160 | margin: -1px;
161 | border: 1px solid #DDDDDD;
162 | background-color: white;
163 | outline: none;
164 | }
165 |
166 | input[type=email] {
167 | width: 230px;
168 | margin-right: 1rem;
169 | }
170 |
171 | input:active {
172 | box-shadow: 0 0 5px #93005a;
173 | border: 1px solid #93005a;
174 | }
175 |
176 | footer {
177 | margin-top: 2rem;
178 | line-height: 2rem;
179 | text-align: center;
180 | color: #93005a;
181 | }
182 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const server = require('./backend/server')
2 |
3 | const PORT = process.env.PORT || 9000
4 |
5 | server.listen(PORT, () => {
6 | console.log(`listening on ${PORT}`)
7 | })
8 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * For a detailed explanation regarding each configuration property, visit:
3 | * https://jestjs.io/docs/configuration
4 | */
5 |
6 | module.exports = {
7 | // All imported modules in your tests should be mocked automatically
8 | // automock: false,
9 |
10 | // Stop running tests after `n` failures
11 | // bail: 0,
12 |
13 | // The directory where Jest should store its cached dependency information
14 | // cacheDirectory: "/private/var/folders/mq/wc13nlwj4gdgcyshdy5j1hgr0000gn/T/jest_dx",
15 |
16 | // Automatically clear mock calls, instances, contexts and results before every test
17 | // clearMocks: false,
18 |
19 | // Indicates whether the coverage information should be collected while executing the test
20 | // collectCoverage: false,
21 |
22 | // An array of glob patterns indicating a set of files for which coverage information should be collected
23 | // collectCoverageFrom: undefined,
24 |
25 | // The directory where Jest should output its coverage files
26 | // coverageDirectory: undefined,
27 |
28 | // An array of regexp pattern strings used to skip coverage collection
29 | // coveragePathIgnorePatterns: [
30 | // "/node_modules/"
31 | // ],
32 |
33 | // Indicates which provider should be used to instrument code for coverage
34 | coverageProvider: "v8",
35 |
36 | // A list of reporter names that Jest uses when writing coverage reports
37 | // coverageReporters: [
38 | // "json",
39 | // "text",
40 | // "lcov",
41 | // "clover"
42 | // ],
43 |
44 | // An object that configures minimum threshold enforcement for coverage results
45 | // coverageThreshold: undefined,
46 |
47 | // A path to a custom dependency extractor
48 | // dependencyExtractor: undefined,
49 |
50 | // Make calling deprecated APIs throw helpful error messages
51 | // errorOnDeprecated: false,
52 |
53 | // The default configuration for fake timers
54 | // fakeTimers: {
55 | // "enableGlobally": false
56 | // },
57 |
58 | // Force coverage collection from ignored files using an array of glob patterns
59 | // forceCoverageMatch: [],
60 |
61 | // A path to a module which exports an async function that is triggered once before all test suites
62 | // globalSetup: undefined,
63 |
64 | // A path to a module which exports an async function that is triggered once after all test suites
65 | // globalTeardown: undefined,
66 |
67 | // A set of global variables that need to be available in all test environments
68 | // globals: {},
69 |
70 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
71 | // maxWorkers: "50%",
72 |
73 | // An array of directory names to be searched recursively up from the requiring module's location
74 | // moduleDirectories: [
75 | // "node_modules"
76 | // ],
77 |
78 | // An array of file extensions your modules use
79 | // moduleFileExtensions: [
80 | // "js",
81 | // "mjs",
82 | // "cjs",
83 | // "jsx",
84 | // "ts",
85 | // "tsx",
86 | // "json",
87 | // "node"
88 | // ],
89 |
90 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
91 | // moduleNameMapper: {},
92 |
93 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
94 | // modulePathIgnorePatterns: [],
95 |
96 | // Activates notifications for test results
97 | // notify: false,
98 |
99 | // An enum that specifies notification mode. Requires { notify: true }
100 | // notifyMode: "failure-change",
101 |
102 | // A preset that is used as a base for Jest's configuration
103 | // preset: undefined,
104 |
105 | // Run tests from one or more projects
106 | // projects: undefined,
107 |
108 | // Use this configuration option to add custom reporters to Jest
109 | // reporters: undefined,
110 |
111 | // Automatically reset mock state before every test
112 | // resetMocks: false,
113 |
114 | // Reset the module registry before running each individual test
115 | // resetModules: false,
116 |
117 | // A path to a custom resolver
118 | // resolver: undefined,
119 |
120 | // Automatically restore mock state and implementation before every test
121 | // restoreMocks: false,
122 |
123 | // The root directory that Jest should scan for tests and modules within
124 | // rootDir: undefined,
125 |
126 | // A list of paths to directories that Jest should use to search for files in
127 | // roots: [
128 | // ""
129 | // ],
130 |
131 | // Allows you to use a custom runner instead of Jest's default test runner
132 | // runner: "jest-runner",
133 |
134 | // The paths to modules that run some code to configure or set up the testing environment before each test
135 | "setupFiles": [
136 | "./jest.globals.js"
137 | ],
138 |
139 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
140 | // setupFilesAfterEnv: [],
141 |
142 | // The number of seconds after which a test is considered as slow and reported as such in the results.
143 | // slowTestThreshold: 5,
144 |
145 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
146 | // snapshotSerializers: [],
147 |
148 | // The test environment that will be used for testing
149 | testEnvironment: "jsdom",
150 |
151 | // Options that will be passed to the testEnvironment
152 | // testEnvironmentOptions: {},
153 |
154 | // Adds a location field to test results
155 | // testLocationInResults: false,
156 |
157 | // The glob patterns Jest uses to detect test files
158 | // testMatch: [
159 | // "**/__tests__/**/*.[jt]s?(x)",
160 | // "**/?(*.)+(spec|test).[tj]s?(x)"
161 | // ],
162 |
163 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
164 | // testPathIgnorePatterns: [
165 | // "/node_modules/"
166 | // ],
167 |
168 | // The regexp pattern or array of patterns that Jest uses to detect test files
169 | // testRegex: [],
170 |
171 | // This option allows the use of a custom results processor
172 | // testResultsProcessor: undefined,
173 |
174 | // This option allows use of a custom test runner
175 | // testRunner: "jest-circus/runner",
176 |
177 | // A map from regular expressions to paths to transformers
178 | // transform: undefined,
179 |
180 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
181 | // transformIgnorePatterns: [
182 | // "/node_modules/",
183 | // "\\.pnp\\.[^\\/]+$"
184 | // ],
185 |
186 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
187 | // unmockedModulePathPatterns: undefined,
188 |
189 | // Indicates whether each individual test should be reported during the run
190 | // verbose: undefined,
191 |
192 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
193 | // watchPathIgnorePatterns: [],
194 |
195 | // Whether to use watchman for file crawling
196 | // watchman: true,
197 | };
198 |
--------------------------------------------------------------------------------
/jest.globals.js:
--------------------------------------------------------------------------------
1 | // Soon Node will have fetch API and this won't be needed
2 | globalThis.fetch = require('node-fetch')
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web-sprint-challenge-advanced-react",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "dev": "fkill :9000 :3000 -s && concurrently \"npm:backend\" \"npm:frontend\"",
6 | "test": "cross-env NODE_ENV=testing jest",
7 | "webpack": "cross-env NODE_ENV=production webpack",
8 | "heroku-postbuild": "npm run webpack",
9 | "frontend": "webpack serve --open",
10 | "backend": "node index.js",
11 | "start": "npm run backend"
12 | },
13 | "devDependencies": {
14 | "@babel/core": "7.18.2",
15 | "@babel/plugin-transform-react-jsx": "7.17.12",
16 | "@babel/plugin-transform-runtime": "7.18.2",
17 | "@babel/preset-env": "7.18.2",
18 | "@babel/preset-react": "7.17.12",
19 | "@testing-library/jest-dom": "5.16.4",
20 | "@testing-library/react": "13.3.0",
21 | "@types/jest": "28.1.1",
22 | "babel-loader": "8.2.5",
23 | "babel-plugin-styled-components": "2.0.7",
24 | "concurrently": "7.2.1",
25 | "cross-env": "7.0.3",
26 | "css-loader": "6.7.1",
27 | "eslint": "8.17.0",
28 | "eslint-plugin-react": "7.30.0",
29 | "fkill-cli": "7.1.0",
30 | "html-loader": "3.1.0",
31 | "html-webpack-plugin": "5.5.0",
32 | "jest": "28.1.1",
33 | "jest-environment-jsdom": "28.1.1",
34 | "msw": "0.42.0",
35 | "nodemon": "2.0.16",
36 | "string-replace-loader": "3.1.0",
37 | "style-loader": "3.3.1",
38 | "webpack": "5.73.0",
39 | "webpack-cli": "4.9.2",
40 | "webpack-dev-server": "4.9.2"
41 | },
42 | "dependencies": {
43 | "axios": "0.27.2",
44 | "cors": "2.8.5",
45 | "express": "4.18.1",
46 | "react": "18.1.0",
47 | "react-dom": "18.1.0",
48 | "react-router-dom": "6.3.0",
49 | "styled-components": "5.3.5",
50 | "yup": "0.32.11"
51 | },
52 | "engines": {
53 | "node": ">=18.x",
54 | "npm": ">=9.x"
55 | },
56 | "repository": {
57 | "type": "git",
58 | "url": "git+https://github.com/bloominstituteoftechnology/web-sprint-challenge-advanced-react.git"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin')
2 | const path = require('path')
3 |
4 | const DEVELOPMENT = 'development'
5 | const ENV = process.env.NODE_ENV || DEVELOPMENT
6 | const IS_DEV = ENV === DEVELOPMENT
7 |
8 | const HTML_LOADER = 'html-loader'
9 | const STYLE_LOADER = 'style-loader'
10 | const CSS_LOADER = 'css-loader'
11 | const BABEL_LOADER = 'babel-loader'
12 | const STRING_REPLACE_LOADER = 'string-replace-loader'
13 |
14 | const SERVER_URL = /http:\/\/localhost:9000/g
15 | const FRONTEND_PORT = 3000
16 |
17 | const INDEX_HTML_PATH = './frontend/index.html'
18 | const INDEX_JS_PATH = './frontend/index.js'
19 | const DIST_FOLDER = 'dist'
20 | const BUNDLE_FILE = 'index.js'
21 |
22 | const SOURCE_MAP = IS_DEV ? 'source-map' : false
23 |
24 | const config = {
25 | entry: INDEX_JS_PATH,
26 | mode: ENV,
27 | output: {
28 | filename: BUNDLE_FILE,
29 | publicPath: '/',
30 | path: path.resolve(__dirname, DIST_FOLDER),
31 | },
32 | devtool: SOURCE_MAP,
33 | plugins: [
34 | new HtmlWebpackPlugin({
35 | template: INDEX_HTML_PATH,
36 | }),
37 | ],
38 | devServer: {
39 | static: path.join(__dirname, DIST_FOLDER),
40 | historyApiFallback: true,
41 | compress: true,
42 | port: FRONTEND_PORT,
43 | client: { logging: 'none' },
44 | },
45 | module: {
46 | rules: [
47 | {
48 | test: /\.html$/i,
49 | exclude: /node_modules/,
50 | use: { loader: HTML_LOADER }
51 | },
52 | {
53 | test: /\.m?js$/,
54 | exclude: /node_modules/,
55 | use: { loader: BABEL_LOADER },
56 | },
57 | {
58 | test: /\.css$/i,
59 | exclude: /node_modules/,
60 | use: [
61 | STYLE_LOADER,
62 | CSS_LOADER,
63 | ],
64 | },
65 | ],
66 | },
67 | }
68 |
69 | if (!IS_DEV) {
70 | config.module.rules.push({
71 | test: /\.m?js$/,
72 | exclude: /node_modules/,
73 | use: {
74 | loader: STRING_REPLACE_LOADER,
75 | options: {
76 | search: SERVER_URL,
77 | replace: '',
78 | },
79 | },
80 | })
81 | }
82 |
83 | module.exports = config
84 |
--------------------------------------------------------------------------------