From d8eda37b09ac35096c0e13c40723e728f7923d63 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 19 Apr 2020 12:39:54 +0200 Subject: [PATCH 1/3] Remove GET / route as it doesn't work (serverRoutes overrides this) --- server/server.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/server/server.js b/server/server.js index 1ced332f68..cd331f4932 100644 --- a/server/server.js +++ b/server/server.js @@ -28,7 +28,6 @@ import embedRoutes from './routes/embed.routes'; import assetRoutes from './routes/asset.routes'; import { requestsOfTypeJSON } from './utils/requestsOfType'; -import { renderIndex } from './views/index'; import { get404Sketch } from './views/404Page'; const app = new Express(); @@ -143,10 +142,6 @@ mongoose.connection.on('error', () => { process.exit(1); }); -app.get('/', (req, res) => { - res.sendFile(renderIndex()); -}); - // Handle API errors app.use('/api', (error, req, res, next) => { if (error && error.code && !res.headersSent) { From e53787e691b667d91d50c8acc1ce4d729a49a414 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 19 Apr 2020 13:35:54 +0200 Subject: [PATCH 2/3] Extract webpack dev mode to own server This runs a new server instance on port 3000 (set using CLIENT_PORT) allowing a developer to only run the client code on their machine. The local editor code can connect to a remote instance of the Editor API by setting the API_URL env var. --- index.js | 8 +- package-lock.json | 185 +++++++++++++++++++--------------- package.json | 7 +- server/server-webpack-only.js | 35 +++++++ server/server.js | 13 +-- webpack/config.dev.js | 3 + 6 files changed, 151 insertions(+), 100 deletions(-) create mode 100644 server/server-webpack-only.js diff --git a/index.js b/index.js index b00b72278a..eb49483546 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,15 @@ if (process.env.NODE_ENV === 'production') { process.env.webpackAssets = JSON.stringify(require('./dist/static/manifest.json')); require('./dist/server.bundle.js'); -} else { +} else{ let parsed = require('dotenv').config(); require('@babel/register')({ presets: ["@babel/preset-env"] }); require('@babel/polyfill'); + + const entryPoint = process.env.USE_CLIENT_APP_SERVER === "true" ? './server/server-webpack-only' : './server/server'; + //// in development, let .env values override those in the environment already (i.e. in docker-compose.yml) // so commenting this out makes the docker container work. // if (process.env.NODE_ENV === 'development') { @@ -14,5 +17,6 @@ if (process.env.NODE_ENV === 'production') { // process.env[key] = parsed[key]; // } // } - require('./server/server'); + + require(entryPoint); } diff --git a/package-lock.json b/package-lock.json index ed49e32088..4478657370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4051,6 +4051,11 @@ "string-width": "^2.0.0" } }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -4197,7 +4202,8 @@ "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true }, "array-flatten": { "version": "1.1.1", @@ -4835,7 +4841,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -5819,6 +5826,57 @@ "typedarray": "^0.0.6" } }, + "concurrently": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-5.1.0.tgz", + "integrity": "sha512-9ViZMu3OOCID3rBgU31mjBftro2chOop0G2u1olq1OuwRBVRw/GxHTg80TVJBUTJfoswMmEUeuOg1g1yu1X2dA==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "date-fns": "^2.0.1", + "lodash": "^4.17.15", + "read-pkg": "^4.0.1", + "rxjs": "^6.5.2", + "spawn-command": "^0.0.2-1", + "supports-color": "^6.1.0", + "tree-kill": "^1.2.2", + "yargs": "^13.3.0" + }, + "dependencies": { + "date-fns": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.12.0.tgz", + "integrity": "sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz", + "integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=", + "dev": true, + "requires": { + "normalize-package-data": "^2.3.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -6943,6 +7001,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, "requires": { "array-find-index": "^1.0.1" } @@ -6952,14 +7011,6 @@ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "requires": { - "es5-ext": "^0.10.9" - } - }, "damerau-levenshtein": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", @@ -7763,26 +7814,6 @@ "is-symbol": "^1.0.2" } }, - "es5-ext": { - "version": "0.10.50", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", - "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "^1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", @@ -7803,15 +7834,6 @@ } } }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -12066,7 +12088,8 @@ "dependencies": { "acorn": { "version": "6.4.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", "dev": true } } @@ -14231,6 +14254,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, "requires": { "chalk": "^2.0.1" } @@ -14273,15 +14297,6 @@ } } }, - "loglevelnext": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", - "requires": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" - } - }, "lolex": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", @@ -14303,6 +14318,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -15742,11 +15758,6 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" - }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -16675,6 +16686,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -21535,7 +21547,8 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "simple-swizzle": { "version": "0.2.2", @@ -21728,7 +21741,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "" + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -21835,6 +21849,12 @@ "memory-pager": "^1.0.2" } }, + "spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "dev": true + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -22753,6 +22773,12 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, "trim-newlines": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", @@ -23123,11 +23149,6 @@ } } }, - "url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" - }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -24124,23 +24145,21 @@ } }, "webpack-dev-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz", - "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", "requires": { - "loud-rejection": "^1.6.0", - "memory-fs": "~0.4.1", - "mime": "^2.1.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "url-join": "^2.0.2", - "webpack-log": "^1.0.1" + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" }, "dependencies": { "mime": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.3.tgz", - "integrity": "sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw==" + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" } } }, @@ -24156,20 +24175,18 @@ } }, "webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" }, "dependencies": { "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, diff --git a/package.json b/package.json index 37420ee980..f21721df85 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "The web editor for p5.js.", "scripts": { "clean": "rimraf dist", - "start": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js", + "start": "concurrently npm:start:server npm:start:client", + "start:server": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development nodemon index.js", + "start:client": "cross-env BABEL_DISABLE_CACHE=1 NODE_ENV=development USE_CLIENT_APP_SERVER=true node index.js", "start:prod": "cross-env NODE_ENV=production node index.js", "lint": "eslint client server --ext .jsx --ext .js", "lint-fix": "eslint client server --ext .jsx --ext .js --fix", @@ -73,6 +75,7 @@ "babel-jest": "^24.9.0", "babel-loader": "^8.0.0", "babel-plugin-transform-react-remove-prop-types": "^0.2.12", + "concurrently": "^5.1.0", "css-loader": "^3.4.2", "cssnano": "^4.1.10", "enzyme": "^3.11.0", @@ -192,7 +195,7 @@ "srcdoc-polyfill": "^0.2.0", "url": "^0.11.0", "webpack": "^4.41.6", - "webpack-dev-middleware": "^2.0.6", + "webpack-dev-middleware": "^3.7.2", "webpack-hot-middleware": "^2.24.3", "xhr": "^2.5.0" } diff --git a/server/server-webpack-only.js b/server/server-webpack-only.js new file mode 100644 index 0000000000..45c83ebd91 --- /dev/null +++ b/server/server-webpack-only.js @@ -0,0 +1,35 @@ +import express from 'express'; +import webpack from 'webpack'; +import webpackDevMiddleware from 'webpack-dev-middleware'; + +import config from '../webpack/config.dev'; +import serverRoutes from './routes/server.routes'; + +const app = express(); + +const compiler = webpack(config); + +// Tell express to use the webpack-dev-middleware and use the webpack.config.js +// configuration file as a base. +app.use(webpackDevMiddleware(compiler, { + publicPath: config.output.publicPath, +})); + +// Server Editor page templates +app.use(serverRoutes); + +const apiPort = process.env.PORT; +const clientPort = process.env.CLIENT_PORT || 3000; + +app.listen(clientPort, () => { + console.log(`Access local Editor at http://localhost:${clientPort} + +You must set API_URL in .env to the editor API e.g. + +API_URL=https://editor.p5js.org/editor + +If you're running the editor API locally, use: + +API_URL=http://localhost:${apiPort}/editor +`); +}); diff --git a/server/server.js b/server/server.js index cd331f4932..6dc083e771 100644 --- a/server/server.js +++ b/server/server.js @@ -9,12 +9,6 @@ import passport from 'passport'; import path from 'path'; import basicAuth from 'express-basic-auth'; -// Webpack Requirements -import webpack from 'webpack'; -import webpackDevMiddleware from 'webpack-dev-middleware'; -import webpackHotMiddleware from 'webpack-hot-middleware'; -import config from '../webpack/config.dev'; - // Import all required modules import api from './routes/api.routes'; import users from './routes/user.routes'; @@ -49,12 +43,7 @@ const corsOriginsWhitelist = [ /p5js\.org$/, ]; -// Run Webpack dev server in development mode if (process.env.NODE_ENV === 'development') { - const compiler = webpack(config); - app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath })); - app.use(webpackHotMiddleware(compiler)); - corsOriginsWhitelist.push(/localhost/); } @@ -170,7 +159,7 @@ app.get('*', (req, res) => { // start app app.listen(process.env.PORT, (error) => { if (!error) { - console.log(`p5js web editor is running on port: ${process.env.PORT}!`); // eslint-disable-line + console.log(`p5js API server is running on port: ${process.env.PORT}!`); // eslint-disable-line } }); diff --git a/webpack/config.dev.js b/webpack/config.dev.js index ee26a4d2d3..4106b7e5bc 100644 --- a/webpack/config.dev.js +++ b/webpack/config.dev.js @@ -27,6 +27,9 @@ module.exports = { filename: '[name].js', publicPath: '/' }, + devServer: { + contentBase: './dist', + }, resolve: { extensions: ['.js', '.jsx'], modules: [ From f009ea951e7d4df03f251d087af9ad62a27206a1 Mon Sep 17 00:00:00 2001 From: Andrew Nicolaou Date: Sun, 3 May 2020 18:15:10 +0200 Subject: [PATCH 3/3] Add installation docs for new method --- developer_docs/installation.md | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/developer_docs/installation.md b/developer_docs/installation.md index 047f21d12c..e974290822 100644 --- a/developer_docs/installation.md +++ b/developer_docs/installation.md @@ -4,6 +4,41 @@ Follow these instructions to set up your development environment, which you need ## Manual Installation +There are two types of manual installation. + +The simplest is to run the front-end web app only (A). This connects to a staging API and allows you to make changes to the web app without having to worry about installing the backend API and database. + +If you need to change the API, then you'll need to run the full-stack (B). + +### A. Front-end web app only + +_Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`. + +1. Install Node.js. The recommended way is to Node through [nvm](https://github.com/nvm-sh/nvm). You can also install [node.js](https://nodejs.org/download/release/v12.16.1/) version 12.16.1 directly from the Node.js website. +2. [Fork](https://help.github.com/articles/fork-a-repo) the [p5.js Web Editor repository](https://github.com/processing/p5.js-web-editor) into your own GitHub account. +3. [Clone](https://help.github.com/articles/cloning-a-repository/) your new fork of the repository from GitHub onto your local computer. + + ``` + $ git clone https://github.com/YOUR_USERNAME/p5.js-web-editor.git + ``` + +4. If you are using nvm, run `$ nvm use` to set your Node version to 12.16.1 +5. Navigate into the project folder and install all its necessary dependencies with npm. + + ``` + $ cd p5.js-web-editor + $ npm install + ``` + +7. `$ cp .env.example .env` +8. Edit `.env` to point to the staging API server `API_URL=https://editor-staging.p5js.org/editor` +9. `$ npm run start:client` +10. Navigate to [http://localhost:8000](http://localhost:8000) in your browser +11. Install the [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) +12. Open and close the Redux DevTools using `ctrl+h`, and move them with `ctrl+w` + +### B. Full-stack: web app, API & database + _Note_: The installation steps assume you are using a Unix-like shell. If you are using Windows, you will need to use `copy` instead of `cp`. 1. Install Node.js. The recommended way is to Node through [nvm](https://github.com/nvm-sh/nvm). You can also install [node.js](https://nodejs.org/download/release/v12.16.1/) version 12.16.1 directly from the Node.js website.