├── .atom-build.yml ├── .babelrc ├── .circleci └── config.yml ├── .eslintrc ├── .gitignore ├── CONTRIBUTING.md ├── GETTING_STARTED.md ├── LICENSE.md ├── README.md ├── client ├── .eslintrc ├── .gitignore ├── config │ ├── env.js │ ├── jest │ │ ├── cssTransform.js │ │ └── fileTransform.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── package.json ├── public │ ├── favicon.ico │ ├── images │ │ ├── defaultAvatar.gif │ │ ├── fcc-banner.png │ │ ├── fcc-logo-green.jpg │ │ ├── fcc-logo.png │ │ ├── fcc_hands_in_logo.svg │ │ ├── fcc_high_five_logo.svg │ │ ├── fcc_learn_logo.svg │ │ └── fcc_portfolio_logo.svg │ ├── index.html │ └── semantic │ │ ├── dist │ │ ├── semantic.min.css │ │ └── themes │ │ │ └── default │ │ │ └── assets │ │ │ ├── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.svg │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ │ │ └── images │ │ │ └── flags.png │ │ └── src │ │ └── site │ │ ├── collections │ │ ├── message.overrides │ │ └── message.variables │ │ └── globals │ │ └── site.variables ├── scripts │ ├── build.js │ ├── start.js │ └── test.js ├── src │ ├── App.js │ ├── App.test.js │ ├── actions │ │ ├── community.js │ │ ├── flashMessages.js │ │ ├── repoValidations.js │ │ ├── scrape-fcc.js │ │ ├── search.js │ │ ├── user.js │ │ └── views.js │ ├── assets │ │ ├── dropdowns │ │ │ ├── countries.js │ │ │ ├── devSurvey.js │ │ │ ├── interests.js │ │ │ ├── repoHosts.js │ │ │ ├── searchTypes.js │ │ │ └── skills.js │ │ └── helpers │ │ │ ├── defineHost.js │ │ │ ├── errors.js │ │ │ ├── filterOptions.js │ │ │ ├── offensive-content │ │ │ └── prohibited-lang.js │ │ │ ├── parseDate.js │ │ │ ├── swearjar-lite.js │ │ │ └── validations.js │ ├── components │ │ ├── AppContainer.js │ │ ├── HomePage.js │ │ ├── Navbar.js │ │ ├── PublicLanding.js │ │ ├── dashboard │ │ │ ├── Account.js │ │ │ ├── Community.js │ │ │ ├── Community │ │ │ │ ├── CertLinks.js │ │ │ │ ├── SocialLinks.js │ │ │ │ └── UserCard.js │ │ │ ├── GitterEmbed.js │ │ │ ├── Landing.js │ │ │ ├── Mentorship.js │ │ │ ├── Mentorship │ │ │ │ ├── SearchFilters.js │ │ │ │ ├── SearchResults.js │ │ │ │ └── UserCard.js │ │ │ ├── Preferences.js │ │ │ ├── Profile │ │ │ │ ├── Preferences │ │ │ │ │ ├── Career.js │ │ │ │ │ ├── Certifications.js │ │ │ │ │ ├── Collaboration.js │ │ │ │ │ ├── Mentorship.js │ │ │ │ │ ├── PersonalInfo.js │ │ │ │ │ ├── SkillsAndInterests.js │ │ │ │ │ ├── Social.js │ │ │ │ │ └── common │ │ │ │ │ │ ├── FormField.js │ │ │ │ │ │ ├── RadioButton.js │ │ │ │ │ │ ├── RepoContainer.js │ │ │ │ │ │ ├── RepoInput.js │ │ │ │ │ │ ├── RepoListItem.js │ │ │ │ │ │ ├── RibbonHeader.js │ │ │ │ │ │ ├── SaveModal.js │ │ │ │ │ │ └── SliderToggle.js │ │ │ │ └── Public │ │ │ │ │ ├── CareerRow.js │ │ │ │ │ ├── FCCTables.js │ │ │ │ │ ├── LocationSteps.js │ │ │ │ │ ├── ProfileHeader.js │ │ │ │ │ ├── SkillsRow.js │ │ │ │ │ ├── SocialList.js │ │ │ │ │ ├── Table.js │ │ │ │ │ └── TableRow.js │ │ │ ├── PublicProfile.js │ │ │ └── common │ │ │ │ ├── ChatIconPopup.js │ │ │ │ ├── DropdownMulti.js │ │ │ │ ├── ListItem.js │ │ │ │ ├── MessageBox.js │ │ │ │ └── UserLabel.js │ │ ├── flash │ │ │ ├── FlashMessage.js │ │ │ └── FlashMessagesList.js │ │ └── signup │ │ │ ├── LoginPage.js │ │ │ └── UserVerification.js │ ├── index.js │ ├── reducers │ │ ├── community.js │ │ ├── flashMessages.js │ │ ├── preferencesViewState.js │ │ ├── publicProfileStats.js │ │ ├── search.js │ │ └── user.js │ ├── rootReducer.js │ └── styles │ │ ├── App.scss │ │ ├── _animations.scss │ │ ├── components │ │ ├── _Profile.scss │ │ └── _SliderToggle.scss │ │ ├── index.scss │ │ └── style-utils.js └── yarn.lock ├── docker-compose.yml ├── package.json ├── sample.env ├── server.js ├── server ├── helpers │ ├── checkHonoraryMemberList.js │ ├── checkWhiteList.js │ ├── getCerts.js │ ├── gitlabRoute.js │ ├── handleProcessedUser.js │ ├── mockData.js │ ├── processCerts.js │ └── safeHandler.js ├── models │ ├── honoraryMember.js │ ├── user.js │ └── whitelistedUser.js └── routes │ ├── community.js │ ├── passport.js │ └── user.js ├── setup.sh └── yarn.lock /.atom-build.yml: -------------------------------------------------------------------------------- 1 | cmd: npm run lint 2 | name: lint 3 | sh: true 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ "es2015" ] 3 | } 4 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | docker: 3 | - image: circleci/node:8.1 4 | # - image: mongo:3 5 | # - image: redis:3.2 6 | 7 | version: 2 8 | jobs: 9 | server-lint: 10 | <<: *defaults 11 | working_directory: ~/alumni-network 12 | steps: 13 | - checkout 14 | - restore_cache: 15 | keys: 16 | # Find a cache corresponding to this specific yarn.lock checksum 17 | - v3-server-deps-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} 18 | # Find a cache corresponding to this specific package.json checksum 19 | - v3-server-deps-{{ .Branch }}-{{ checksum "package.json" }} 20 | # Find the most recent cache used from this branch 21 | - v3-server-deps-{{ .Branch }} 22 | # Find the most recent cache used from master 23 | - v3-server-deps-master 24 | - run: 25 | name: Install dependencies 26 | command: yarn 27 | no_output_timeout: 3m 28 | - run: 29 | name: Run linter 30 | command: npm run lint 31 | no_output_timeout: 1m 32 | - save_cache: 33 | paths: 34 | - node_modules 35 | # Save cache for this specific set of packages 36 | key: v3-server-deps-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} 37 | 38 | client-lint: 39 | <<: *defaults 40 | working_directory: ~/alumni-network/client 41 | steps: 42 | - checkout: 43 | path: ~/alumni-network 44 | - restore_cache: 45 | keys: 46 | # Find a cache corresponding to this specific yarn.lock checksum 47 | - v3-client-deps-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} 48 | # Find a cache corresponding to this specific package.json checksum 49 | - v3-client-deps-{{ .Branch }}-{{ checksum "package.json" }} 50 | # Find the most recent cache used from this branch 51 | - v3-client-deps-{{ .Branch }} 52 | # Find the most recent cache used from master 53 | - v3-client-deps-master 54 | - run: 55 | name: Install dependencies 56 | command: yarn 57 | no_output_timeout: 3m 58 | - run: 59 | name: Run linter 60 | command: npm run lint 61 | no_output_timeout: 1m 62 | - save_cache: 63 | paths: 64 | - node_modules 65 | # Save cache for this specific set of packages 66 | key: v3-client-deps-{{ .Branch }}-{{ checksum "package.json" }}-{{ checksum "yarn.lock" }} 67 | 68 | workflows: 69 | version: 2 70 | checkout_lint: 71 | jobs: 72 | - server-lint 73 | - client-lint 74 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | extends: react-app 3 | plugins: 4 | - import 5 | - jsx-a11y 6 | rules: 7 | array-callback-return: 0 8 | max-len: 9 | - warn 10 | - 85 11 | - 2 12 | - ignoreUrls: true 13 | ignoreTemplateLiterals: true 14 | ignoreStrings: true 15 | ignoreComments: true 16 | sort-imports: 17 | - error 18 | - ignoreCase: true 19 | ignoreMemberSort: false 20 | memberSyntaxSortOrder: [none, all, single, multiple] 21 | import/newline-after-import: 2 22 | import/no-duplicates: 2 23 | import/named: 2 24 | jsx-a11y/href-no-hash: off 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | **/node_modules 3 | .vscode 4 | 5 | # testing 6 | /coverage 7 | 8 | # production 9 | /client/build 10 | 11 | # remove css compiled on the fly 12 | # from source control 13 | src/**/*.css 14 | 15 | # misc 16 | .DS_Store 17 | .env 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | dump.rdb 23 | server/dump.rdb 24 | -------------------------------------------------------------------------------- /GETTING_STARTED.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | #### Setting Up Your System 4 | 5 | 1. Install [Git](https://git-scm.com/) or your favorite Git client. 6 | 2. (Optional) [Setup an SSH Key](https://help.github.com/articles/generating-an-ssh-key/) for GitHub. 7 | 3. Create a parent projects directory on your system. 8 | 9 | #### Forking freeCodeCamp Alumni Network 10 | 11 | 1. Go to the freeCodeCamp Alumni Network (FFCAN) repository: 12 | 2. Click the "Fork" Button in the upper right hand corner of the interface ([More Details Here](https://help.github.com/articles/fork-a-repo/)) 13 | 3. After the repository has been forked, you will be taken to your copy of the FCCAN repo at `yourUsername/alumni-network` 14 | 15 | #### Cloning Your Fork 16 | 17 | 1. Open a Terminal / Command Line / Bash Shell in your projects directory (_i.e.: `/yourprojectdirectory/`_) 18 | 2. Clone your fork of FCCAN 19 | 20 | ```shell 21 | $ git clone https://github.com//alumni-network.git 22 | ``` 23 | 24 | ##### (make sure to replace `` with your GitHub Username) 25 | 26 | This will download the entire FCCAN repo to your project's directory. 27 | 28 | #### Setup Your Upstream 29 | 30 | 1. Change directory to the new FCCAN directory (`cd alumni-network`) 31 | 2. Add a remote to the official FCCAN repo: 32 | 33 | ```shell 34 | $ git remote add upstream https://github.com/FCC-Alumni/alumni-network.git 35 | ``` 36 | 37 | Congratulations, you now have a local copy of the FCCAN repo! 38 | 39 | #### Maintaining Your Fork 40 | 41 | Now that you have a copy of your fork, there is work you will need to do to keep it current. 42 | 43 | ##### **Rebasing from Upstream** 44 | 45 | Do this prior to every time you create a branch for a PR: 46 | 47 | 1. Make sure you are on the `master` branch 48 | 49 | ```shell 50 | $ git status 51 | On branch master 52 | Your branch is up-to-date with 'origin/master'. 53 | ``` 54 | > _If your aren't on `master`, resolve outstanding files / commits and checkout the `master` branch_ 55 | 56 | ```shell 57 | $ git checkout master 58 | ``` 59 | 60 | 2. Do a pull with rebase against `upstream` 61 | 62 | ```shell 63 | $ git pull --rebase upstream master 64 | ``` 65 | > _This will pull down all of the changes to the official master branch, without making an additional commit in your local repo._ 66 | 67 | 3. (_Optional_) Force push your updated master branch to your GitHub fork 68 | 69 | ```shell 70 | $ git push origin master --force 71 | ``` 72 | > This will overwrite the master branch of your fork. 73 | 74 | ### Create A Branch 75 | 76 | Before you start working, you will need to create a separate branch specific to the issue / feature you're working on. You will push your work to this branch. 77 | 78 | #### Naming Your Branch 79 | 80 | Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example `fix/email-login` would be a branch where you fix something specific to email login. 81 | 82 | #### Adding Your Branch 83 | 84 | To create a branch on your local machine (and switch to this branch): 85 | 86 | ```shell 87 | $ git checkout -b [name_of_your_new_branch] 88 | ``` 89 | For example: 90 | ```shell 91 | $ git checkout -b fix/minor-typo 92 | ``` 93 | and to push to GitHub: 94 | 95 | ```shell 96 | $ git push origin fix/minor-type 97 | ``` 98 | 99 | ##### If you need more help with branching, take a look at _[this](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)_. 100 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, freeCodeCamp-alumni-network 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | *** 2 | ## Fun while it lasted... 3 | This was a fun project, but it's time to call it quits and archive this repo (considering it's been dormant for quite some time now and the server's been down for a few years :smile:). Thanks to everyone who helped build FCCAN and to our users when we were live! I hope everyone had as much fun as I did. 4 | 5 | Be well and stay safe out there, 6 | 7 | Pete 8 | *** 9 | 10 | ## The official home of the freeCodeCamp Alumni Network Repo 11 | ![Image of freeCodeCamp Alumni Network](http://i.imgur.com/hRxbclK.png) 12 | The [freeCodeCamp Alumni Network](http://fcc-alumni.com)(FCCAN) is a MERN stack single-page web application built as an extension of the freeCodeCamp community by freeCodeCampers who are passionate about open source software, learning, collaboration, and React. Check us out and come along for the ride! If you are interested in contributing, please read our [contributing guidelines](https://github.com/FCC-Alumni/alumni-network/blob/master/CONTRIBUTING.md) before jumping in. Thanks! 13 | 14 | \- FCCAN Team 15 | 16 | *** 17 | 18 | freeCodeCamp has an incredible and vibrant international community. We built this app specifically to try and cultivate relationships among experienced campers. 19 | 20 | Currently, the [FCC Forum](https://forum.freecodecamp.org), [Gitter](https://gitter.im/freeCodeCamp), and other resources provide ample opportunities for campers at any skill level. We wanted to create an environment specifically for more experienced campers who are looking for advanced collaborative projects or mentorship opportunities, as a mentor or mentee. 21 | 22 | Our authentication process verifies the FCC progress of users, and only admits students who have completed at least one FCC Certificate (Note: User's will have to make their FCC profiles public for this validation to work). 23 | 24 | Our goal is to create a focused community of like-minded individuals who can benefit from each others culminated experience and expertise, whether in new technologies, programming skills, or career advice. 25 | 26 | *** 27 | 28 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 29 | -------------------------------------------------------------------------------- /client/.eslintrc: -------------------------------------------------------------------------------- 1 | --- 2 | extends: 3 | - react-app 4 | plugins: [jsx-a11y, react, import] 5 | rules: 6 | max-len: 7 | - warn 8 | - 85 9 | - 2 10 | - ignoreUrls: true 11 | ignoreTemplateLiterals: true 12 | ignoreStrings: true 13 | ignoreComments: true 14 | no-console: [error, {allow: [warn, error]}] 15 | sort-imports: 16 | - error 17 | - ignoreCase: true 18 | ignoreMemberSort: false 19 | memberSyntaxSortOrder: [none, all, single, multiple] 20 | sort-keys: [warn, asc, {caseSensitive: false, natural: true}] 21 | sort-vars: [error, {ignoreCase: true}] 22 | ############################ 23 | # disable unresolveable rule 24 | jsx-a11y/href-no-hash: off 25 | ##################### 26 | # eslint-plugin-react 27 | react/jsx-closing-bracket-location: 28 | - error 29 | - after-props 30 | react/jsx-closing-tag-location: 2 31 | react/jsx-equals-spacing: [2, never] 32 | react/jsx-no-bind: 33 | - error 34 | - ignoreRefs: true 35 | allowArrowFunctions: true 36 | allowBind: false 37 | react/jsx-no-literals: 2 38 | react/jsx-no-target-blank: 2 39 | react/jsx-pascal-case: 2 40 | react/jsx-sort-props: error 41 | react/jsx-tag-spacing: 42 | - 2 43 | - closingSlash: never 44 | beforeSelfClosing: always 45 | afterOpening: never 46 | react/jsx-wrap-multilines: 2 47 | react/no-array-index-key: 2 48 | react/self-closing-comp: 2 49 | ###################### 50 | # eslint-plugin-import 51 | import/newline-after-import: 2 52 | import/no-duplicates: 2 53 | import/named: 2 54 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | **/node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # remove css compiled on the fly 11 | # from source control 12 | src/**/*.css 13 | 14 | # misc 15 | .DS_Store 16 | **/.env 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | -------------------------------------------------------------------------------- /client/config/env.js: -------------------------------------------------------------------------------- 1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 2 | // injected into the application via DefinePlugin in Webpack configuration. 3 | 4 | var REACT_APP = /^REACT_APP_/i; 5 | 6 | function getClientEnvironment(publicUrl) { 7 | var raw = Object 8 | .keys(process.env) 9 | .filter(key => REACT_APP.test(key)) 10 | .reduce((env, key) => { 11 | env[key] = process.env[key]; 12 | return env; 13 | }, { 14 | // Useful for determining whether we’re running in production mode. 15 | // Most importantly, it switches React into the correct mode. 16 | 'NODE_ENV': process.env.NODE_ENV || 'development', 17 | // Useful for resolving the correct path to static assets in `public`. 18 | // For example, . 19 | // This should only be used as an escape hatch. Normally you would put 20 | // images into the `src` and `import` them in code to get their paths. 21 | 'PUBLIC_URL': publicUrl 22 | }); 23 | // Stringify all values so we can feed into Webpack DefinePlugin 24 | var stringified = { 25 | 'process.env': Object 26 | .keys(raw) 27 | .reduce((env, key) => { 28 | env[key] = JSON.stringify(raw[key]); 29 | return env; 30 | }, {}) 31 | }; 32 | 33 | return { raw, stringified }; 34 | } 35 | 36 | module.exports = getClientEnvironment; 37 | -------------------------------------------------------------------------------- /client/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | // This is a custom Jest transformer turning style imports into empty objects. 2 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 3 | 4 | module.exports = { 5 | process() { 6 | return 'module.exports = {};'; 7 | }, 8 | getCacheKey(fileData, filename) { 9 | // The output is always the same. 10 | return 'cssTransform'; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /client/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // This is a custom Jest transformer turning file imports into filenames. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process(src, filename) { 8 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';'; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /client/config/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | var url = require('url'); 4 | 5 | // Make sure any symlinks in the project folder are resolved: 6 | // https://github.com/facebookincubator/create-react-app/issues/637 7 | var appDirectory = fs.realpathSync(process.cwd()); 8 | function resolveApp(relativePath) { 9 | return path.resolve(appDirectory, relativePath); 10 | } 11 | 12 | // We support resolving modules according to `NODE_PATH`. 13 | // This lets you use absolute paths in imports inside large monorepos: 14 | // https://github.com/facebookincubator/create-react-app/issues/253. 15 | 16 | // It works similar to `NODE_PATH` in Node itself: 17 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 18 | 19 | // We will export `nodePaths` as an array of absolute paths. 20 | // It will then be used by Webpack configs. 21 | // Jest doesn’t need this because it already handles `NODE_PATH` out of the box. 22 | 23 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 24 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 25 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 26 | 27 | var nodePaths = (process.env.NODE_PATH || '') 28 | .split(process.platform === 'win32' ? ';' : ':') 29 | .filter(Boolean) 30 | .filter(folder => !path.isAbsolute(folder)) 31 | .map(resolveApp); 32 | 33 | var envPublicUrl = process.env.PUBLIC_URL; 34 | 35 | function ensureSlash(path, needsSlash) { 36 | var hasSlash = path.endsWith('/'); 37 | if (hasSlash && !needsSlash) { 38 | return path.substr(path, path.length - 1); 39 | } else if (!hasSlash && needsSlash) { 40 | return path + '/'; 41 | } else { 42 | return path; 43 | } 44 | } 45 | 46 | function getPublicUrl(appPackageJson) { 47 | return envPublicUrl || require(appPackageJson).homepage; 48 | } 49 | 50 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 51 | // "public path" at which the app is served. 52 | // Webpack needs to know it to put the right