├── .travis.yml
├── src
├── .eslintrc
├── test.js
├── styles.scss
└── index.js
├── example
├── demo.gif
├── src
│ ├── index.js
│ ├── helpers.js
│ ├── index.css
│ ├── mocks.js
│ └── App.js
├── README.md
├── public
│ ├── manifest.json
│ └── index.html
├── config
│ ├── jest
│ │ ├── cssTransform.js
│ │ └── fileTransform.js
│ ├── pnpTs.js
│ ├── modules.js
│ ├── paths.js
│ ├── env.js
│ ├── webpackDevServer.config.js
│ └── webpack.config.js
├── scripts
│ ├── test.js
│ ├── start.js
│ └── build.js
└── package.json
├── .babelrc
├── .editorconfig
├── .gitignore
├── .eslintrc
├── .github
└── FUNDING.yml
├── rollup.config.js
├── LICENSE
├── package.json
├── CONTRIBUTING.md
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 9
4 | - 8
5 |
--------------------------------------------------------------------------------
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/example/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dsternlicht/react-dynamic-charts/HEAD/example/demo.gif
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false
5 | }],
6 | "stage-0",
7 | "react"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/test.js:
--------------------------------------------------------------------------------
1 | import ExampleComponent from './'
2 |
3 | describe('ExampleComponent', () => {
4 | it('is truthy', () => {
5 | expect(ExampleComponent).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 |
4 | import './index.css'
5 | import App from './App'
6 |
7 | ReactDOM.render(, document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # react-dynamic-charts - example app
2 |
3 | ## Installing dependencies
4 |
5 | `npm i`
6 |
7 | ## Running
8 |
9 | `npm start`
10 |
11 | The app will be available by default in **port 3000**.
--------------------------------------------------------------------------------
/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "react-dynamic-charts",
3 | "name": "react-dynamic-charts",
4 | "start_url": "./index.html",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "standard",
5 | "standard-react"
6 | ],
7 | "env": {
8 | "es6": true
9 | },
10 | "plugins": [
11 | "react"
12 | ],
13 | "parserOptions": {
14 | "sourceType": "module"
15 | },
16 | "rules": {
17 | "space-before-function-paren": 0,
18 | "react/jsx-boolean-value": 0,
19 | "semi": 0,
20 | "jsx-quotes": 0
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | react-dynamic-charts
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [dsternlicht]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/example/config/pnpTs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { resolveModuleName } = require('ts-pnp');
4 |
5 | exports.resolveModuleName = (
6 | typescript,
7 | moduleName,
8 | containingFile,
9 | compilerOptions,
10 | resolutionHost
11 | ) => {
12 | return resolveModuleName(
13 | moduleName,
14 | containingFile,
15 | compilerOptions,
16 | resolutionHost,
17 | typescript.resolveModuleName
18 | );
19 | };
20 |
21 | exports.resolveTypeReferenceDirective = (
22 | typescript,
23 | moduleName,
24 | containingFile,
25 | compilerOptions,
26 | resolutionHost
27 | ) => {
28 | return resolveModuleName(
29 | moduleName,
30 | containingFile,
31 | compilerOptions,
32 | resolutionHost,
33 | typescript.resolveTypeReferenceDirective
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/example/src/helpers.js:
--------------------------------------------------------------------------------
1 | function getRandomNumber(min, max) {
2 | return Math.floor(Math.random() * (max - min + 1) + min);
3 | };
4 |
5 | function generateData(iterations = 100, defaultValues = [], namePrefix = {}, maxJump = 100) {
6 | const arr = [];
7 | for (let i = 0; i <= iterations; i++) {
8 | const values = defaultValues.map((v, idx) => {
9 | if (i === 0 && typeof v.value === 'number') {
10 | return v;
11 | }
12 | return {
13 | ...v,
14 | value: i === 0 ? this.getRandomNumber(1, 1000) : arr[i - 1].values[idx].value + this.getRandomNumber(0, maxJump)
15 | }
16 | });
17 | arr.push({
18 | name: `${namePrefix.prefix || ''} ${(namePrefix.initialValue || 0) + i}`,
19 | values
20 | });
21 | }
22 | return arr;
23 | };
24 |
25 | export default {
26 | getRandomNumber,
27 | generateData
28 | }
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel'
2 | import commonjs from 'rollup-plugin-commonjs'
3 | import external from 'rollup-plugin-peer-deps-external'
4 | import postcss from 'rollup-plugin-postcss'
5 | import resolve from 'rollup-plugin-node-resolve'
6 | import url from 'rollup-plugin-url'
7 | import svgr from '@svgr/rollup'
8 |
9 | import pkg from './package.json'
10 |
11 | export default {
12 | input: 'src/index.js',
13 | output: [
14 | {
15 | file: pkg.main,
16 | format: 'cjs',
17 | sourcemap: true
18 | },
19 | {
20 | file: pkg.module,
21 | format: 'es',
22 | sourcemap: true
23 | }
24 | ],
25 | plugins: [
26 | external(),
27 | postcss({
28 | modules: false,
29 | extract: true
30 | }),
31 | url(),
32 | svgr(),
33 | babel({
34 | exclude: 'node_modules/**',
35 | plugins: [ 'external-helpers' ]
36 | }),
37 | resolve(),
38 | commonjs()
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Daniel Sternlicht
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/src/index.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap');
2 |
3 | body {
4 | margin: 0;
5 | padding: 0;
6 | font-family: 'Open Sans', sans-serif;
7 | }
8 |
9 | *:focus {
10 | outline: 0;
11 | }
12 |
13 | button {
14 | font-size: 16px;
15 | background: #333;
16 | color: #fff;
17 | border: none;
18 | border-radius: 5px;
19 | padding: 10px 20px;
20 | cursor: pointer;
21 | }
22 |
23 | button:hover {
24 | background: #111;
25 | }
26 |
27 | .main-section {
28 | position: relative;
29 | }
30 |
31 | .main-section > a {
32 | position: fixed;
33 | z-index: 1000;
34 | top: 0px;
35 | right: 0px;
36 | border: 0px;
37 | }
38 |
39 | .chart-row {
40 | display: flex;
41 | justify-content: flex-start;
42 | align-items: stretch;
43 | background: #f8f8f8;
44 | }
45 |
46 | .chart-row .description {
47 | min-width: 420px;
48 | max-width: 420px;
49 | box-sizing: border-box;
50 | padding: 20px;
51 | border-right: 1px solid #ccc;
52 | }
53 |
54 | .chart-row .description h3 {
55 | margin: 0 0 20px;
56 | }
57 |
58 | .chart-row .description .buttons {
59 | text-align: center;
60 | margin-bottom: 20px;
61 | }
62 |
63 | .chart-row .description .buttons > * {
64 | margin: 0 8px;
65 | }
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--watchAll') === -1
45 | ) {
46 | // https://github.com/facebook/create-react-app/issues/5210
47 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
48 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
49 | }
50 |
51 |
52 | jest.run(argv);
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-dynamic-charts",
3 | "version": "1.0.5",
4 | "description": "",
5 | "author": "dsternlicht",
6 | "license": "MIT",
7 | "repository": "dsternlicht/react-dynamic-charts",
8 | "main": "dist/index.js",
9 | "module": "dist/index.es.js",
10 | "jsnext:main": "dist/index.es.js",
11 | "engines": {
12 | "node": ">=8",
13 | "npm": ">=5"
14 | },
15 | "scripts": {
16 | "test": "cross-env CI=1 react-scripts test --env=jsdom",
17 | "test:watch": "react-scripts test --env=jsdom",
18 | "build": "rollup -c",
19 | "start": "rollup -c -w",
20 | "prepare": "npm run build",
21 | "predeploy": "cd example && npm install && npm run build",
22 | "deploy": "gh-pages -d example/build"
23 | },
24 | "peerDependencies": {
25 | "prop-types": "^15.5.4",
26 | "react": "^15.0.0 || ^16.0.0",
27 | "react-dom": "^15.0.0 || ^16.0.0"
28 | },
29 | "devDependencies": {
30 | "@svgr/rollup": "^2.4.1",
31 | "babel-core": "^6.26.3",
32 | "babel-plugin-external-helpers": "^6.22.0",
33 | "babel-preset-env": "^1.7.0",
34 | "babel-preset-react": "^6.24.1",
35 | "babel-preset-stage-0": "^6.24.1",
36 | "cross-env": "^5.1.4",
37 | "eslint": "^5.0.1",
38 | "eslint-config-standard": "^11.0.0",
39 | "eslint-config-standard-react": "^6.0.0",
40 | "eslint-plugin-import": "^2.13.0",
41 | "eslint-plugin-node": "^7.0.1",
42 | "eslint-plugin-promise": "^4.0.0",
43 | "eslint-plugin-react": "^7.10.0",
44 | "eslint-plugin-standard": "^3.1.0",
45 | "gh-pages": "^1.2.0",
46 | "node-sass": "^4.12.0",
47 | "react": "^16.8.6",
48 | "react-dom": "^16.8.6",
49 | "react-scripts": "^3.0.1",
50 | "rollup": "^0.64.1",
51 | "rollup-plugin-babel": "^3.0.7",
52 | "rollup-plugin-commonjs": "^9.1.3",
53 | "rollup-plugin-node-resolve": "^3.3.0",
54 | "rollup-plugin-peer-deps-external": "^2.2.0",
55 | "rollup-plugin-postcss": "^1.6.2",
56 | "rollup-plugin-url": "^1.4.0"
57 | },
58 | "files": [
59 | "dist"
60 | ]
61 | }
62 |
--------------------------------------------------------------------------------
/example/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 |
8 | /**
9 | * Get the baseUrl of a compilerOptions object.
10 | *
11 | * @param {Object} options
12 | */
13 | function getAdditionalModulePaths(options = {}) {
14 | const baseUrl = options.baseUrl;
15 |
16 | // We need to explicitly check for null and undefined (and not a falsy value) because
17 | // TypeScript treats an empty string as `.`.
18 | if (baseUrl == null) {
19 | // If there's no baseUrl set we respect NODE_PATH
20 | // Note that NODE_PATH is deprecated and will be removed
21 | // in the next major release of create-react-app.
22 |
23 | const nodePath = process.env.NODE_PATH || '';
24 | return nodePath.split(path.delimiter).filter(Boolean);
25 | }
26 |
27 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
28 |
29 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is
30 | // the default behavior.
31 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
32 | return null;
33 | }
34 |
35 | // Allow the user set the `baseUrl` to `appSrc`.
36 | if (path.relative(paths.appSrc, baseUrlResolved) === '') {
37 | return [paths.appSrc];
38 | }
39 |
40 | // Otherwise, throw an error.
41 | throw new Error(
42 | chalk.red.bold(
43 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." +
44 | ' Create React App does not support other values at this time.'
45 | )
46 | );
47 | }
48 |
49 | function getModules() {
50 | // Check if TypeScript is setup
51 | const hasTsConfig = fs.existsSync(paths.appTsConfig);
52 | const hasJsConfig = fs.existsSync(paths.appJsConfig);
53 |
54 | if (hasTsConfig && hasJsConfig) {
55 | throw new Error(
56 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
57 | );
58 | }
59 |
60 | let config;
61 |
62 | // If there's a tsconfig.json we assume it's a
63 | // TypeScript project and set up the config
64 | // based on tsconfig.json
65 | if (hasTsConfig) {
66 | config = require(paths.appTsConfig);
67 | // Otherwise we'll check if there is jsconfig.json
68 | // for non TS projects.
69 | } else if (hasJsConfig) {
70 | config = require(paths.appJsConfig);
71 | }
72 |
73 | config = config || {};
74 | const options = config.compilerOptions || {};
75 |
76 | const additionalModulePaths = getAdditionalModulePaths(options);
77 |
78 | return {
79 | additionalModulePaths: additionalModulePaths,
80 | hasTsConfig,
81 | };
82 | }
83 |
84 | module.exports = getModules();
85 |
--------------------------------------------------------------------------------
/src/styles.scss:
--------------------------------------------------------------------------------
1 | .live-chart {
2 | width: 100%;
3 | padding: 20px;
4 | box-sizing: border-box;
5 | position: relative;
6 | text-align: center;
7 |
8 | .start-button {
9 | margin: 0 auto;
10 | }
11 |
12 | h1 {
13 | font-weight: 700;
14 | font-size: 60px;
15 | text-transform: uppercase;
16 | text-align: center;
17 | padding: 20px 10px;
18 | margin: 0;
19 | }
20 |
21 | .chart {
22 | position: relative;
23 | margin: 20px auto;
24 | }
25 |
26 | .baseline {
27 | position: absolute;
28 | top: 0;
29 | left: 50%;
30 | width: 2px;
31 | height: 100%;
32 | z-index: 2;
33 | background: #555;
34 |
35 | span {
36 | position: absolute;
37 | left: 0;
38 | top: -25px;
39 | transform: translateX(-50%);
40 | font-style: italic;
41 | }
42 | }
43 |
44 | .chart-bars {
45 | position: relative;
46 | width: 100%;
47 |
48 | &.with-baseline {
49 | .bar-wrapper {
50 | left: 0;
51 | padding-left: 50%;
52 |
53 | label {
54 | right: 50%;
55 | width: auto;
56 | }
57 |
58 | &.behind-baseline {
59 | left: auto;
60 | right: 0;
61 | padding-left: 0;
62 | padding-right: 50%;
63 | flex-direction: row-reverse;
64 |
65 | label {
66 | right: auto;
67 | left: 50%;
68 | text-align: left;
69 | flex-direction: row-reverse;
70 |
71 | img {
72 | margin: 0 10px 0 0;
73 | }
74 | }
75 |
76 | .value {
77 | right: 100%;
78 | left: auto;
79 | margin-left: 0;
80 | margin-right: 10px;
81 | }
82 | }
83 | }
84 | }
85 | }
86 |
87 | .bar-wrapper {
88 | display: flex;
89 | flex-wrap: wrap;
90 | align-items: center;
91 | position: absolute;
92 | top: 0;
93 | left: 0;
94 | transform: translateY(0);
95 | transition: transform 0.5s linear;
96 | padding-left: 200px;
97 | box-sizing: border-box;
98 | width: 100%;
99 | justify-content: flex-start;
100 |
101 | label {
102 | position: absolute;
103 | height: 100%;
104 | width: 200px;
105 | left: 0;
106 | padding: 0 10px;
107 | box-sizing: border-box;
108 | text-align: right;
109 | top: 50%;
110 | transform: translateY(-50%);
111 | font-size: 16px;
112 | font-weight: 700;
113 | display: flex;
114 | justify-content: flex-end;
115 | align-items: center;
116 |
117 | img {
118 | max-height: 100%;
119 | max-width: 100%;
120 | margin-left: 10px;
121 | }
122 | }
123 |
124 | .value {
125 | font-size: 16px;
126 | font-weight: 700;
127 | margin-left: 10px;
128 | }
129 |
130 | .bar {
131 | width: 0%;
132 | transition: width 0.5s linear;
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/example/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
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 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${inputPath}/`;
20 | } else {
21 | return inputPath;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right