├── .dockerignore ├── .env ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── .prettierignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── config ├── env.js ├── getHttpsConfig.js ├── jest │ ├── cssTransform.js │ ├── fileTransform.js │ └── setEnvVars.js ├── modules.js ├── paths.js ├── pnpTs.js ├── webpack.config.js └── webpackDevServer.config.js ├── docs ├── configuration.md ├── custom-covers.md ├── example-timemap.png └── mapbox.md ├── example.config.js ├── index.html ├── package-lock.json ├── package.json ├── public └── index.html ├── scripts ├── build.js ├── start.js └── test.js ├── src ├── actions │ └── index.js ├── assets │ ├── arrowdown.svg │ ├── bellingcat-logo.png │ ├── checkbox.svg │ ├── close.svg │ ├── fa-logo.png │ ├── fonts │ │ └── timemapfont.woff │ ├── placeholder-image.jpg │ └── satelliteoverlaytoggle │ │ ├── map.png │ │ └── sat.png ├── common │ ├── constants.js │ ├── data │ │ ├── copy.json │ │ └── es-MX.json │ ├── global.js │ └── utilities.js ├── components │ ├── .DS_Store │ ├── App.js │ ├── InfoPopup.js │ ├── Layout.js │ ├── Notification.js │ ├── TemplateCover.js │ ├── Toolbar.js │ ├── atoms │ │ ├── Checkbox.js │ │ ├── CoeventIcon.js │ │ ├── ColoredMarkers.js │ │ ├── Content.js │ │ ├── Controls.js │ │ ├── CoverIcon.js │ │ ├── InfoIcon.js │ │ ├── Loading.js │ │ ├── Md.js │ │ ├── Media.js │ │ ├── NoSource.js │ │ ├── Popup.js │ │ ├── RefreshIcon.js │ │ ├── RouteIcon.js │ │ ├── SitesIcon.js │ │ ├── Spinner.js │ │ └── StaticPage.js │ ├── controls │ │ ├── BottomActions.js │ │ ├── Card.js │ │ ├── CardStack.js │ │ ├── CategoriesListPanel.js │ │ ├── FilterListPanel.js │ │ ├── FullScreenToggle.js │ │ ├── NarrativeControls.js │ │ ├── Search.js │ │ ├── ShapesListPanel.js │ │ └── atoms │ │ │ ├── Button.js │ │ │ ├── Caret.js │ │ │ ├── CustomField.js │ │ │ ├── Media.js │ │ │ ├── NarrativeAdjust.js │ │ │ ├── NarrativeCard.js │ │ │ ├── NarrativeClose.js │ │ │ ├── PanelTree.js │ │ │ ├── SearchRow.jsx │ │ │ ├── TelegramEmbed.js │ │ │ ├── Text.js │ │ │ ├── Time.js │ │ │ └── ToolbarButton.js │ ├── space │ │ ├── Space.js │ │ └── carto │ │ │ ├── Map.js │ │ │ └── atoms │ │ │ ├── Clusters.js │ │ │ ├── DefsMarkers.js │ │ │ ├── Events.js │ │ │ ├── Narratives.js │ │ │ ├── Regions.js │ │ │ ├── SatelliteOverlayToggle.js │ │ │ ├── SelectedEvents.js │ │ │ ├── Sites.js │ │ │ └── __tests__ │ │ │ └── SatelliteOverlayToggle.spec.js │ └── time │ │ ├── Axis.js │ │ ├── Categories.js │ │ ├── Timeline.js │ │ └── atoms │ │ ├── Clip.js │ │ ├── DatetimeBar.js │ │ ├── DatetimeDot.js │ │ ├── DatetimePentagon.js │ │ ├── DatetimeSquare.js │ │ ├── DatetimeStar.js │ │ ├── DatetimeTriangle.js │ │ ├── Events.js │ │ ├── Handles.js │ │ ├── Header.js │ │ ├── Labels.js │ │ ├── Markers.js │ │ ├── Project.js │ │ └── ZoomControls.js ├── index.jsx ├── reducers │ ├── __tests__ │ │ └── ui.spec.js │ ├── app.js │ ├── domain.js │ ├── features.js │ ├── index.js │ ├── root.js │ ├── ui.js │ └── validate │ │ ├── associationsSchema.js │ │ ├── eventSchema.js │ │ ├── regionSchema.js │ │ ├── shapeSchema.js │ │ ├── siteSchema.js │ │ ├── sourceSchema.js │ │ └── validators.js ├── scss │ ├── _burger.scss │ ├── _icons.scss │ ├── _variables.scss │ ├── _video.scss │ ├── button.scss │ ├── card.scss │ ├── cardstack.scss │ ├── common.scss │ ├── cover.scss │ ├── header.scss │ ├── infopopup.scss │ ├── loading.scss │ ├── main.scss │ ├── map.scss │ ├── mediaplayer.scss │ ├── narrativecard.scss │ ├── notification.scss │ ├── overlay.scss │ ├── popup.scss │ ├── satelliteoverlaytoggle.scss │ ├── search.scss │ ├── tabs.scss │ ├── timeline.scss │ ├── toolbar.scss │ └── video.scss ├── selectors │ ├── helpers.js │ └── index.js ├── setupTests.js ├── store │ ├── index.js │ ├── initial.js │ └── plugins │ │ └── urlState │ │ ├── applyUrlState.js │ │ ├── index.js │ │ ├── middleware.js │ │ ├── schema.js │ │ └── urlState.js └── test │ └── App.test.js └── test └── server_process.js /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | example.config.js 4 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | FAST_REFRESH=true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Environment 7 | ----------- 8 | 9 | * Your version (in package.json) or git commit hash 10 | * Your operating system and version: 11 | 12 | 13 | 14 | Steps to reproduce (for bugs only) 15 | ----------------------------- 16 | 17 | 18 | 19 | 1. 20 | 2. 21 | 3. 22 | 23 | Current Behavior 24 | ---------------- 25 | 26 | 27 | 28 | 29 | Expected Behavior 30 | ----------------- 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | on: 3 | push: 4 | branches: [ develop ] 5 | # pull_request: 6 | # branches: [ develop ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Trigger CD build 13 | uses: peter-evans/repository-dispatch@v1 14 | with: 15 | token: ${{ secrets.CI_DISPATCH_TOKEN }} 16 | repository: forensic-architecture/configs 17 | event-type: remote-build 18 | client-payload: '{"runtime_args": "timemap", "branch": "${GITHUB_REF##*/}"}' 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ develop ] 5 | pull_request: 6 | branches: [ develop ] 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | ref: ${{ github.head_ref }} 15 | - uses: actions/setup-node@v2-beta 16 | with: 17 | node-version: '12' 18 | 19 | - run: npm install 20 | - run: cp example.config.js config.js 21 | - run: npm test 22 | env: 23 | CI: true 24 | - run: npm run lint 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | build/ 3 | node_modules/ 4 | 5 | *config.js 6 | dev.config.js 7 | !config/webpack*.config.js 8 | !config/getHttpsConfig.js 9 | 10 | 11 | tags 12 | tags.lock 13 | tags.temp 14 | 15 | .eslintcache 16 | 17 | src/\.DS_Store 18 | src/assets/fonts 19 | 20 | \.DS_Store 21 | 22 | package-lock.json 23 | tags 24 | 25 | .env.local 26 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore Create React App-related scaffolding 2 | config/ 3 | scripts/ 4 | test/server_process.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | cache: 5 | directories: 6 | - node_modules 7 | before_script: 8 | - cp example.config.js config.js 9 | install: 10 | - npm install 11 | script: 12 | - npm run lint 13 | - npm run build 14 | - npm run test 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to timemap 2 | 3 | Hi there! Thank you already, for taking the time to contribute and improve 4 | timemap. This document is right place to start. Read it thoroughly! 5 | 6 | ## What do I need to know to help? 7 | ### Javascript / React / Redux 8 | In order to contribute code upstream, you'll likely need to have a sense of ES6 9 | Javascript, React, and Redux. If these terms are new to you, or not as familiar 10 | as you might like, here's a good tutorial to get you up to speed: 11 | 12 | - [Building a voting app with Redux and React](https://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html) 13 | 14 | ### Node JS and Docker 15 | Timemap doesn't actually use these technologies; but the main way of getting up 16 | and running with a data provider for timemap, 17 | [datasheet-server](https://github.com/forensic-architecture/datasheet-server), 18 | does, and so they're helpful to know. 19 | 20 | ## Do I need to be an experienced JS developer? 21 | Contributing can of course be about contributing code, but it can also take 22 | many other forms. A great amount of work that remains to be done to make 23 | timemap a usable community tool doesn't involve writing any code. The following 24 | are all very welcome contributions: 25 | 26 | - Writing, updating or correcting documentation 27 | - Fixing an open issue 28 | - Requesting a feature 29 | - Reporting a bug 30 | 31 | If you're new to this project, you could check the issues that are tagged 32 | ["good first issue"](https://github.com/forensic-architecture/timemap/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). 33 | 34 | These are a range of the issues that have come up in conversation for which we 35 | would welcome community contributions. These are, however, by no means 36 | exhaustive! If you see a gap or have an idea, please open up an issue to 37 | discuss it with timemap's maintainers. 38 | 39 | ## How do I make a contribution? 40 | 41 | 1. Make sure you have a [GitHub account](https://github.com/signup/free) 42 | 2. Fork the repository on GitHub. This is necessary so that you can push your 43 | changes, as you can't do this directly on our repo. 44 | 45 | 3. Get set up with a local instance of timemap and datasheet-server. The easiest 46 | way to do this is by reading [this blog post on our website](https://forensic-architecture.org/investigation/timemap-for-cartographic-platforms). 47 | 4. [Join our Discord server](https://discord.gg/PjHKHJD5KX). Here you'll be able 48 | to track commits that are actively being made across our projects; but more 49 | importantly it's where you can ask questions if something's not clear or 50 | not working as you expect. The #timemap and #support channels are the two 51 | best places to ask questions about setting timemap up. 52 | 53 | Once you're set up with a local copy of timemap and datasheet-server, you can 54 | start modifying code and making changes. 55 | 56 | When you're ready to submit a contribution, you can do it by making a pull 57 | request from a branch on your forked copy of timemap to this repository. You 58 | can do this with the following steps: 59 | 1. Push the changes to a remote repository. If the changes you have made 60 | address a bug, you should name it `bug/{briefdesc}`, where `{briefdesc}` is 61 | a hyphen-separated description of your change. If instead you are 62 | contributing changes as a feature request, name it `feature/{briefdesc`}. If 63 | in doubt, prefix your branch with `feature/`. 64 | 2. Submit a pull request to the `develop` branch of `forensic-architecture/timemap`. 65 | 3. Wait for the pull request to be reviewed by a maintainer. 66 | 4. Make changes to the pull request if the reviewing maintainer recommends 67 | them. 68 | 5. Celebrate your success once your pull request is merged! 69 | 70 | ## How do I validate my changes? 71 | We are still working on a set of tests. Right now, it is enough to confirm that 72 | the application runs as expected with `npm run dev`. If your changes introduce 73 | other issues, a maintainer will flag it in stage 3 of the submission process 74 | above. 75 | 76 | ## Credits 77 | This contributing guide is based on the guidelines of both the 78 | [SuperCollider contributing guide](https://raw.githubusercontent.com/supercollider/supercollider/develop/CONTRIBUTING.md), 79 | and the [nteract contributing 80 | guide](https://github.com/nteract/nteract/blob/master/CONTRIBUTING.md) (two 81 | excellent open source projects!). 82 | 83 | Thanks to [Scott Carver](https://github.com/scztt) for advice on how to put 84 | a guide together. 85 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:10.11 2 | 3 | LABEL authors="Lachlan Kermode " 4 | 5 | # Install app dependencies 6 | COPY package.json /www/package.json 7 | RUN cd /www; yarn 8 | 9 | # Copy app source 10 | COPY . /www 11 | WORKDIR /www 12 | RUN yarn build 13 | 14 | # files available to copy at /www/build 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Do No Harm License 2 | 3 | **Preamble** 4 | 5 | Most software today is developed with little to no thought of how it will be used, or the consequences for our society and planet. 6 | 7 | As software developers, we engineer the infrastructure of the 21st century. We recognise that our infrastructure has great power to shape the world and the lives of those we share it with, and we choose to consciously take responsibility for the social and environmental impacts of what we build. 8 | 9 | We envisage a world free from injustice, inequality, and the reckless destruction of lives and our planet. We reject slavery in all its forms, whether by force, indebtedness, or by algorithms that hack human vulnerabilities. We seek a world where humankind is at peace with our neighbours, nature, and ourselves. We want our work to enrich the physical, mental and spiritual wellbeing of all society. 10 | 11 | We build software to further this vision of a just world, or at the very least, to not put that vision further from reach. 12 | 13 | **Terms** 14 | 15 | *Copyright* (c) 2019 Forensic Architecture. All rights reserved. 16 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 17 | 18 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 19 | 20 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 21 | 22 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 23 | 24 | 4. This software must not be used by any organisation, website, product or service that: 25 | 26 | a) lobbies for, promotes, or derives a majority of income from actions that support or contribute to: 27 | * sex trafficking 28 | * human trafficking 29 | * slavery 30 | * indentured servitude 31 | * gambling 32 | * tobacco 33 | * adversely addictive behaviours 34 | * nuclear energy 35 | * warfare 36 | * weapons manufacturing 37 | * war crimes 38 | * violence (except when required to protect public safety) 39 | * burning of forests 40 | * deforestation 41 | * hate speech or discrimination based on age, gender, gender identity, race, sexuality, religion, nationality 42 | 43 | b) lobbies against, or derives a majority of income from actions that discourage or frustrate: 44 | * peace 45 | * access to the rights set out in the Universal Declaration of Human Rights and the Convention on the Rights of the Child 46 | * peaceful assembly and association (including worker associations) 47 | * a safe environment or action to curtail the use of fossil fuels or prevent climate change 48 | * democratic processes 49 | 50 | 5. All redistribution of source code or binary form, including any modifications must be under these terms. You must inform recipients that the code is governed by these conditions, and how they can obtain a copy of this license. You may not attempt to alter the conditions of who may/may not use this software. 51 | 52 | We define: 53 | 54 | **Forests** to be 0.5 or more hectares of trees that were either planted more than 50 years ago or were not planted by humans or human made equipment. 55 | 56 | **Deforestation** to be the clearing, burning or destruction of 0.5 or more hectares of forests within a 1 year period. 57 | 58 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 | 60 | **Attribution** 61 | 62 | Do No Harm License [Contributor Covenant][homepage], (pre 1.0), 63 | available at https://github.com/raisely/NoHarm 64 | 65 | [homepage]: https://github.com/raisely/NoHarm 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | TimeMap v0 3 |

4 | 5 |

PLEASE NOTE THAT THIS REPOSITORY IS NO LONGER ACTIVELY MAINTAINED. For a more active fork, see this project.

6 | 7 |

8 | TimeMap is a tool for exploration, monitoring and classification of incidents in time and space.
See a live instance here.

9 |

10 | 11 | ![](docs/example-timemap.png) 12 | 13 | [![Build status](https://travis-ci.com/forensic-architecture/timemap.svg?branch=develop)](https://travis-ci.com/forensic-architecture/timemap) 14 | 15 | ## Overview 16 | 17 | TimeMap is a standalone frontend application that allows to explore and monitor events in time and space. TimeMap uses OpenStreetMap satellite imagery as a backdrop by default, but can also be configured to use [mapbox](https://www.mapbox.com/). It uses Leaflet and d3 to visually map information. 18 | 19 | The recommended way to run a backend for timemap is using [datasheet-server](https://github.com/forensic-architecture/datasheet-server). This allows you to work with a spreadsheet or Google Sheet as a dynamic database for for timemap. 20 | 21 | TimeMap has the following high-level features capabilites: 22 | 23 | - Visualize incidents of particular events on a map. 24 | - Visualize and filter these incidents over time, on an adjustable timeline that allows to zoom in and out. 25 | - Visualize types of incidents by tag and by category, which can be displayed using different styles. 26 | 27 | A fully-functioning live version can be found as a result of the Forensic Architecture investigation of the [Battle of Ilovaisk](https://ilovaisk.forensic-architecture.org). 28 | 29 | ## Get up and running 30 | 31 | These easiest way to get up and running with timemap and datasheet-server is to 32 | [follow the in-depth tutorial here](https://forensic-architecture.org/investigation/timemap-for-cartographic-platforms). 33 | 34 | We recommend using **Node v16.x.x** for its current compatibility. 35 | TimeMap may not work with other versions of Node. 36 | 37 | ### Quickstart 38 | 39 | 1. Pull this repository. 40 | 41 | ```shell 42 | git clone https://github.com/forensic-architecture/timemap 43 | ``` 44 | 45 | 2. Install dependencies via npm. 46 | 47 | ```shell 48 | npm install 49 | ``` 50 | 51 | 3. Copy the example config 52 | 53 | ```shell 54 | cp example.config.js config.js 55 | ``` 56 | 57 | 4. Run the development server, which will be available at http://localhost:8080. 58 | 59 | ```shell 60 | CONFIG=config.js npm run dev 61 | ``` 62 | 63 | To run with a file that is not 'config.js' in the root directory, set the `CONFIG` environment variable: 64 | 65 | ``` 66 | CONFIG="myotherconfig.js" npm run dev 67 | ``` 68 | 69 | At this stage, you'll probably only see a basic map with several error modals. In order for TimeMap to be able to display interesting information, you'll have to make sure to have the capacity to serve data, as well as adjusting some configuration parameters. See the [in-depth tutorial](https://forensic-architecture.org/investigation/timemap-for-cartographic-platforms) or [datasheet-server](https://github.com/forensic-architecture/datasheet-server). 70 | 71 | #### Running without datasheet-server 72 | 73 | Technically, timemap is backend agnostic, but it requires a series of endpoints to provide data for it to visualize. The data is expected in JSON format. Some data elements are required and their format has some required fields. Other additional endpoints are optional, and if enabled, they simply add features to your taste. 74 | 75 | The combination of all these data types is called the `domain` of the application in the context of TimeMap. 76 | 77 | #### Running tests 78 | 79 | We are currently using [Jest](https://jestjs.io/) for front-end and component testing. These tests can be found inside `src/test`. The test suite can be invoked through `CONFIG="my-optional-config.js" npm run test`. 80 | 81 | We also include an [Ava](https://github.com/avajs/ava) test suite for smoke testing the Node server process responsible for instantiating the app. This test suite can be invoked using `CONFIG="my-optional-config.js" npm run test:ava` 82 | 83 | ### Contributing 84 | 85 | Interested in helping us improve timemap? See [our contributing guide](CONTRIBUTING.md) to learn how to contribute and make suggestions. Please also read our [code of conduct](CODE_OF_CONDUCT.md). We endeavour to cultivate a community around timemap and other OSS at Forensic Architecture that is inclusive and respectful. Please join us in this! 86 | 87 | ## Community 88 | 89 | If you have any questions or just want to chat, please [join our Discord server](https://discord.gg/PjHKHJD5KX). This is where you can ask questions, as well as track our internal development on timemap and other codebases at Forensic Architecture. 90 | 91 | ## [License](LICENSE.md) 92 | 93 | timemap is distributed under the [DoNoHarm license](https://github.com/raisely/NoHarm). 94 | -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const paths = require("./paths"); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve("./paths")]; 9 | 10 | // START - injecting env variables from config.js 11 | // This section is responsible for require-ing the provided 12 | // command-line argument configuration file and serializing it. 13 | // It will later be available inside our app under process.env, 14 | // through webpack.definePlugin 15 | const CONFIG = process.env.CONFIG || "config.js"; 16 | const envConfig = require("../" + CONFIG); 17 | 18 | const userConfig = {}; 19 | for (const k in envConfig) { 20 | userConfig[k] = JSON.stringify(envConfig[k]); 21 | } 22 | // END - injecting env variables from config.js 23 | 24 | const NODE_ENV = process.env.NODE_ENV; 25 | if (!NODE_ENV) { 26 | throw new Error( 27 | "The NODE_ENV environment variable is required but was not specified." 28 | ); 29 | } 30 | 31 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 32 | const dotenvFiles = [ 33 | `${paths.dotenv}.${NODE_ENV}.local`, 34 | // Don't include `.env.local` for `test` environment 35 | // since normally you expect tests to produce the same 36 | // results for everyone 37 | NODE_ENV !== "test" && `${paths.dotenv}.local`, 38 | `${paths.dotenv}.${NODE_ENV}`, 39 | paths.dotenv, 40 | ].filter(Boolean); 41 | 42 | // Load environment variables from .env* files. Suppress warnings using silent 43 | // if this file is missing. dotenv will never modify any environment variables 44 | // that have already been set. Variable expansion is supported in .env files. 45 | // https://github.com/motdotla/dotenv 46 | // https://github.com/motdotla/dotenv-expand 47 | dotenvFiles.forEach((dotenvFile) => { 48 | if (fs.existsSync(dotenvFile)) { 49 | require("dotenv-expand")( 50 | require("dotenv").config({ 51 | path: dotenvFile, 52 | }) 53 | ); 54 | } 55 | }); 56 | 57 | // We support resolving modules according to `NODE_PATH`. 58 | // This lets you use absolute paths in imports inside large monorepos: 59 | // https://github.com/facebook/create-react-app/issues/253. 60 | // It works similar to `NODE_PATH` in Node itself: 61 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 62 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 63 | // Otherwise, we risk importing Node.js core modules into an app instead of webpack shims. 64 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 65 | // We also resolve them to make sure all tools using them work consistently. 66 | const appDirectory = fs.realpathSync(process.cwd()); 67 | process.env.NODE_PATH = (process.env.NODE_PATH || "") 68 | .split(path.delimiter) 69 | .filter((folder) => folder && !path.isAbsolute(folder)) 70 | .map((folder) => path.resolve(appDirectory, folder)) 71 | .join(path.delimiter); 72 | 73 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 74 | // injected into the application via DefinePlugin in webpack configuration. 75 | const REACT_APP = /^REACT_APP_/i; 76 | 77 | function getClientEnvironment(publicUrl) { 78 | const raw = Object.keys(process.env) 79 | .filter((key) => REACT_APP.test(key)) 80 | .reduce( 81 | (env, key) => { 82 | env[key] = process.env[key]; 83 | return env; 84 | }, 85 | { 86 | // Useful for determining whether we’re running in production mode. 87 | // Most importantly, it switches React into the correct mode. 88 | NODE_ENV: process.env.NODE_ENV || "development", 89 | // Useful for resolving the correct path to static assets in `public`. 90 | // For example, . 91 | // This should only be used as an escape hatch. Normally you would put 92 | // images into the `src` and `import` them in code to get their paths. 93 | PUBLIC_URL: publicUrl, 94 | // We support configuring the sockjs pathname during development. 95 | // These settings let a developer run multiple simultaneous projects. 96 | // They are used as the connection `hostname`, `pathname` and `port` 97 | // in webpackHotDevClient. They are used as the `sockHost`, `sockPath` 98 | // and `sockPort` options in webpack-dev-server. 99 | WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST, 100 | WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH, 101 | WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT, 102 | // Whether or not react-refresh is enabled. 103 | // react-refresh is not 100% stable at this time, 104 | // which is why it's disabled by default. 105 | // It is defined here so it is available in the webpackHotDevClient. 106 | FAST_REFRESH: process.env.FAST_REFRESH !== "false", 107 | } 108 | ); 109 | // Stringify all values so we can feed into webpack DefinePlugin 110 | const stringified = { 111 | "process.env": Object.keys(raw).reduce( 112 | (env, key) => { 113 | env[key] = JSON.stringify(raw[key]); 114 | return env; 115 | }, 116 | { 117 | ...userConfig, 118 | } 119 | ), 120 | }; 121 | 122 | return { raw, stringified }; 123 | } 124 | 125 | module.exports = getClientEnvironment; 126 | -------------------------------------------------------------------------------- /config/getHttpsConfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const crypto = require('crypto'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const paths = require('./paths'); 8 | 9 | // Ensure the certificate and key provided are valid and if not 10 | // throw an easy to debug error 11 | function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { 12 | let encrypted; 13 | try { 14 | // publicEncrypt will throw an error with an invalid cert 15 | encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); 16 | } catch (err) { 17 | throw new Error( 18 | `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}` 19 | ); 20 | } 21 | 22 | try { 23 | // privateDecrypt will throw an error with an invalid key 24 | crypto.privateDecrypt(key, encrypted); 25 | } catch (err) { 26 | throw new Error( 27 | `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ 28 | err.message 29 | }` 30 | ); 31 | } 32 | } 33 | 34 | // Read file and throw an error if it doesn't exist 35 | function readEnvFile(file, type) { 36 | if (!fs.existsSync(file)) { 37 | throw new Error( 38 | `You specified ${chalk.cyan( 39 | type 40 | )} in your env, but the file "${chalk.yellow(file)}" can't be found.` 41 | ); 42 | } 43 | return fs.readFileSync(file); 44 | } 45 | 46 | // Get the https config 47 | // Return cert files if provided in env, otherwise just true or false 48 | function getHttpsConfig() { 49 | const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env; 50 | const isHttps = HTTPS === 'true'; 51 | 52 | if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) { 53 | const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE); 54 | const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE); 55 | const config = { 56 | cert: readEnvFile(crtFile, 'SSL_CRT_FILE'), 57 | key: readEnvFile(keyFile, 'SSL_KEY_FILE'), 58 | }; 59 | 60 | validateKeyAndCerts({ ...config, keyFile, crtFile }); 61 | return config; 62 | } 63 | return isHttps; 64 | } 65 | 66 | module.exports = getHttpsConfig; 67 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return "module.exports = {};"; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return "cssTransform"; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | const camelcase = require("camelcase"); 5 | 6 | // This is a custom Jest transformer turning file imports into filenames. 7 | // http://facebook.github.io/jest/docs/en/webpack.html 8 | 9 | module.exports = { 10 | process(src, filename) { 11 | const assetFilename = JSON.stringify(path.basename(filename)); 12 | 13 | if (filename.match(/\.svg$/)) { 14 | // Based on how SVGR generates a component name: 15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 16 | const pascalCaseFilename = camelcase(path.parse(filename).name, { 17 | pascalCase: true, 18 | }); 19 | const componentName = `Svg${pascalCaseFilename}`; 20 | return `const React = require('react'); 21 | module.exports = { 22 | __esModule: true, 23 | default: ${assetFilename}, 24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) { 25 | return { 26 | $$typeof: Symbol.for('react.element'), 27 | type: 'svg', 28 | ref: ref, 29 | key: null, 30 | props: Object.assign({}, props, { 31 | children: ${assetFilename} 32 | }) 33 | }; 34 | }), 35 | };`; 36 | } 37 | 38 | return `module.exports = ${assetFilename};`; 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /config/jest/setEnvVars.js: -------------------------------------------------------------------------------- 1 | const envConfig = require("../../" + (process.env.CONFIG || 'config.js')); 2 | process.env = { ...process.env, ...envConfig }; 3 | -------------------------------------------------------------------------------- /config/modules.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | const chalk = require('react-dev-utils/chalk'); 7 | const resolve = require('resolve'); 8 | 9 | /** 10 | * Get additional module paths based on the baseUrl of a compilerOptions object. 11 | * 12 | * @param {Object} options 13 | */ 14 | function getAdditionalModulePaths(options = {}) { 15 | const baseUrl = options.baseUrl; 16 | 17 | if (!baseUrl) { 18 | return ''; 19 | } 20 | 21 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 22 | 23 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is 24 | // the default behavior. 25 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { 26 | return null; 27 | } 28 | 29 | // Allow the user set the `baseUrl` to `appSrc`. 30 | if (path.relative(paths.appSrc, baseUrlResolved) === '') { 31 | return [paths.appSrc]; 32 | } 33 | 34 | // If the path is equal to the root directory we ignore it here. 35 | // We don't want to allow importing from the root directly as source files are 36 | // not transpiled outside of `src`. We do allow importing them with the 37 | // absolute path (e.g. `src/Components/Button.js`) but we set that up with 38 | // an alias. 39 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 40 | return null; 41 | } 42 | 43 | // Otherwise, throw an error. 44 | throw new Error( 45 | chalk.red.bold( 46 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 47 | ' Create React App does not support other values at this time.' 48 | ) 49 | ); 50 | } 51 | 52 | /** 53 | * Get webpack aliases based on the baseUrl of a compilerOptions object. 54 | * 55 | * @param {*} options 56 | */ 57 | function getWebpackAliases(options = {}) { 58 | const baseUrl = options.baseUrl; 59 | 60 | if (!baseUrl) { 61 | return {}; 62 | } 63 | 64 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 65 | 66 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 67 | return { 68 | src: paths.appSrc, 69 | }; 70 | } 71 | } 72 | 73 | /** 74 | * Get jest aliases based on the baseUrl of a compilerOptions object. 75 | * 76 | * @param {*} options 77 | */ 78 | function getJestAliases(options = {}) { 79 | const baseUrl = options.baseUrl; 80 | 81 | if (!baseUrl) { 82 | return {}; 83 | } 84 | 85 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl); 86 | 87 | if (path.relative(paths.appPath, baseUrlResolved) === '') { 88 | return { 89 | '^src/(.*)$': '/src/$1', 90 | }; 91 | } 92 | } 93 | 94 | function getModules() { 95 | // Check if TypeScript is setup 96 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 97 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 98 | 99 | if (hasTsConfig && hasJsConfig) { 100 | throw new Error( 101 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 102 | ); 103 | } 104 | 105 | let config; 106 | 107 | // If there's a tsconfig.json we assume it's a 108 | // TypeScript project and set up the config 109 | // based on tsconfig.json 110 | if (hasTsConfig) { 111 | const ts = require(resolve.sync('typescript', { 112 | basedir: paths.appNodeModules, 113 | })); 114 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; 115 | // Otherwise we'll check if there is jsconfig.json 116 | // for non TS projects. 117 | } else if (hasJsConfig) { 118 | config = require(paths.appJsConfig); 119 | } 120 | 121 | config = config || {}; 122 | const options = config.compilerOptions || {}; 123 | 124 | const additionalModulePaths = getAdditionalModulePaths(options); 125 | 126 | return { 127 | additionalModulePaths: additionalModulePaths, 128 | webpackAliases: getWebpackAliases(options), 129 | jestAliases: getJestAliases(options), 130 | hasTsConfig, 131 | }; 132 | } 133 | 134 | module.exports = getModules(); 135 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 13 | // "public path" at which the app is served. 14 | // webpack needs to know it to put the right