├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.json
├── commitlint.config.js
├── config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── webpack.config.js
└── webpackDevServer.config.js
├── gulpfile.js
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── remove-dist.js
├── scripts
├── build.js
├── check-yarn.js
├── release.sh
├── start.js
└── test.js
├── src
├── demo
│ ├── App.tsx
│ ├── index.less
│ └── index.tsx
├── lib
│ ├── components
│ │ ├── anchor-wrapper
│ │ │ ├── __tests__
│ │ │ │ └── index.tsx
│ │ │ └── index.tsx
│ │ ├── context.ts
│ │ ├── line
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── index.tsx.snap
│ │ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ └── lineText.tsx
│ │ ├── minimap
│ │ │ ├── child.tsx
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── node-wrapper
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── index.tsx.snap
│ │ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── selection
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── snapline
│ │ │ └── index.tsx
│ │ ├── template-wrapper
│ │ │ ├── __tests__
│ │ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ └── topology
│ │ │ ├── __tests__
│ │ │ ├── __snapshots__
│ │ │ │ └── index.tsx.snap
│ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ ├── config.ts
│ ├── declare.ts
│ ├── index.tsx
│ └── utils
│ │ ├── Graph.ts
│ │ ├── __tests__
│ │ ├── computeLayout.ts
│ │ ├── deleteSelectedData.ts
│ │ ├── index.ts
│ │ ├── selectNodes.ts
│ │ └── tree.ts
│ │ ├── computeLayout.ts
│ │ ├── dagre.type.d.ts
│ │ ├── deleteSelectedData.ts
│ │ ├── index.ts
│ │ ├── selectNodes.ts
│ │ └── tree.ts
└── react-app-env.d.ts
├── transform.js
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | config/
3 | dist/
4 | *.js
5 | *.d.ts
6 | *.test.tsx
7 | react-app-env.d.ts
8 | src/lib/components/topology/index.tsx
9 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | settings: {
7 | "import/extensions": [".ts", ".tsx"],
8 | "import/resolver": {
9 | typescript: {},
10 | },
11 | "import/parsers": {
12 | "@typescript-eslint/parser": [".ts", ".tsx"],
13 | },
14 | },
15 | parser: "@typescript-eslint/parser",
16 | extends: [
17 | "eslint:recommended",
18 | "plugin:@typescript-eslint/recommended",
19 | "plugin:jsx-a11y/recommended",
20 | "airbnb",
21 | ],
22 | globals: {
23 | Atomics: "readonly",
24 | SharedArrayBuffer: "readonly",
25 | },
26 | parserOptions: {
27 | ecmaFeatures: {
28 | jsx: true,
29 | },
30 | ecmaVersion: 2018,
31 | sourceType: "module",
32 | errorOnTypeScriptSyntaticAndSemanticIssues: false,
33 | },
34 | plugins: ["react", "import", "jsx-a11y", "@typescript-eslint"],
35 | rules: {
36 | indent: ["error", 4, { SwitchCase: 1 }],
37 | "max-len": ["error", { code: 150 }],
38 | "react/sort-comp": 0,
39 | "react/prop-types": 0,
40 | "import/no-unresolved": 0,
41 | "import/extensions": 0,
42 | "arrow-body-style": 0,
43 | "arrow-parens": 0,
44 | "import/prefer-default-export": 1,
45 | "import/no-cycle": 0,
46 | "react/jsx-indent": ["error", 4],
47 | "react/no-did-update-set-state": 0,
48 | "react/jsx-indent-props": ["error", 4],
49 | "react/prefer-stateless-function": 0,
50 | "react/jsx-props-no-spreading": 0,
51 | "react/static-property-placement": 0,
52 | "jsx-a11y/click-events-have-key-events": 0,
53 | "jsx-a11y/no-static-element-interactions": 0,
54 | "@typescript-eslint/no-use-before-define": "off",
55 | "@typescript-eslint/ban-ts-comment": "off",
56 | "@typescript-eslint/ban-types": "off",
57 | "@typescript-eslint/no-explicit-any": "off",
58 | "no-use-before-define": "off",
59 | "no-shadow": "off",
60 | "no-mixed-operators": 0,
61 | "no-undef": 0,
62 | "no-console": "off",
63 | "react/jsx-filename-extension": [1, { extensions: [".ts", ".tsx"] }],
64 | "react/state-in-constructor": 0,
65 | "@typescript-eslint/no-empty-interface": 0,
66 | "@typescript-eslint/interface-name-prefix": 0,
67 | "@typescript-eslint/explicit-function-return-type": 0,
68 | "@typescript-eslint/explicit-member-accessibility": 0,
69 | "@typescript-eslint/no-non-null-assertion": 0,
70 | "@typescript-eslint/no-object-literal-type-assertion": 0,
71 | "@typescript-eslint/no-unused-vars": [
72 | "error",
73 | { vars: "all", args: "after-used", ignoreRestSiblings: true },
74 | ],
75 | quotes: 0,
76 | "comma-dangle": "off",
77 | "no-else-return": ["error", { allowElseIf: true }],
78 | "react/destructuring-assignment": [
79 | "warn",
80 | "always",
81 | { ignoreClassFields: true },
82 | ],
83 | "react/require-default-props": [0],
84 | "react/destructuring-assignment": [0],
85 | "react/no-unused-prop-types": [0],
86 | },
87 | };
88 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Triggers the workflow on push or pull request events but only for the master branch
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 | branches: [master]
11 |
12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
13 | jobs:
14 | setup:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: checkout
18 | uses: actions/checkout@v2
19 |
20 | - name: cache yarn.lock
21 | uses: actions/cache@v2
22 | with:
23 | path: package-temp-dir
24 | key: lock-${{ github.sha }}
25 |
26 | - name: create yarn.lock
27 | run: yarn generate-lock-entry
28 |
29 | - name: hack for single file
30 | run: |
31 | if [ ! -d "package-temp-dir" ]; then
32 | mkdir package-temp-dir
33 | fi
34 | cp yarn.lock package-temp-dir
35 | - name: cache node_modules
36 | id: node_modules_cache_id
37 | uses: actions/cache@v2
38 | with:
39 | path: node_modules
40 | key: node_modules-${{ hashFiles('**/package-temp-dir/yarn.lock') }}
41 |
42 | - name: install
43 | if: steps.node_modules_cache_id.outputs.cache-hit != 'true'
44 | run: yarn
45 |
46 | static-checking:
47 | needs: [setup]
48 | runs-on: ubuntu-latest
49 | steps:
50 | - uses: actions/checkout@v2
51 |
52 | - name: Restore cache from yarn.lock
53 | uses: actions/cache@v2
54 | with:
55 | path: package-temp-dir
56 | key: lock-${{ github.sha }}
57 |
58 | - name: Restore cache from node_modules
59 | uses: actions/cache@v2
60 | with:
61 | path: node_modules
62 | key: node_modules-${{ hashFiles('**/package-temp-dir/yarn.lock') }}
63 |
64 | - name: Run ESlint
65 | run: yarn lint
66 |
67 | test:
68 | runs-on: ubuntu-latest
69 | needs: [setup]
70 | steps:
71 | - uses: actions/checkout@v2
72 |
73 | - name: Restore cache from yarn.lock
74 | uses: actions/cache@v2
75 | with:
76 | path: package-temp-dir
77 | key: lock-${{ github.sha }}
78 |
79 | - name: Restore cache from node_modules
80 | uses: actions/cache@v2
81 | with:
82 | path: node_modules
83 | key: node_modules-${{ hashFiles('**/package-temp-dir/yarn.lock') }}
84 |
85 | - name: Run Unit Test
86 | run: yarn test
87 | # build:
88 | # runs-on: ubuntu-latest
89 | # needs: [setup, static-checking, test]
90 | # steps:
91 | # - uses: actions/checkout@v2
92 |
93 | # - name: Restore cache from yarn.lock
94 | # uses: actions/cache@v2
95 | # with:
96 | # path: package-temp-dir
97 | # key: lock-${{ github.sha }}
98 |
99 | # - name: Restore cache from node_modules
100 | # uses: actions/cache@v2
101 | # with:
102 | # path: node_modules
103 | # key: node_modules-${{ hashFiles('**/package-temp-dir/yarn.lock') }}
104 |
105 | # - name: Run Build
106 | # run: yarn build
107 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 |
3 | ##排除打包文件
4 | **/build
5 | **/dist
6 |
7 | ## 排除NodeJs的加载模块
8 | **/node_modules
9 |
10 | ## 排除happyback
11 | **/.happypack
12 |
13 | ## 排除npm-debug.log
14 | **/npm-debug.log
15 | **/npm-debug.log.*
16 |
17 | ## 排除rev文件干扰
18 | **/.vscode
19 |
20 | **/doc
21 |
22 | **/coverage
23 |
24 | **/export_sheet
25 | **/logs
26 | .cache-loader
27 |
28 | # 排除package-lock.json
29 | package-lock.json
30 | yarn-error.log
31 |
32 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/byai/topology/7ee4f119ef0bfd02f283b4dbeea00dc7dc2f1432/.npmrc
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 BaiYing AI
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Topology
2 |
3 | ## 安装
4 |
5 | ```bash
6 | yarn add @byai/topology
7 | ```
8 |
9 | ## 开发
10 |
11 | ```bash
12 | yarn
13 | yarn start
14 | ```
15 |
16 | ## 测试
17 |
18 | ```bash
19 | yarn test
20 | ```
21 |
22 | ## 示例
23 |
24 | http://localhost:3000
25 |
26 |
27 | ## 使用
28 |
29 | 见demo
30 |
31 | ## 组件
32 |
33 | ### Topology
34 |
35 | #### props
36 |
37 |
38 |
39 |
40 | name |
41 | type |
42 | default |
43 | description |
44 |
45 |
46 |
47 |
48 | data |
49 | object |
50 | { nodes: []; lines: [] } |
51 | 数据 |
52 |
53 |
54 | snapline |
55 | bool |
56 | true |
57 | 是否显示辅助对齐线 |
58 |
59 |
60 | lineLinkageHighlight |
61 | boolean |
62 | false |
63 | hover 节点线条是否联动高亮 |
64 |
65 |
66 | lineColor |
67 | object |
68 | {} |
69 | 线条颜色映射对象 eg: {'锚点1': '#82BEFF', '锚点2': '#FFA39E'} |
70 |
71 |
72 | lineTextColor |
73 | object |
74 | {} |
75 | 线条上文字颜色映射对象 eg: {'锚点1': '#82BEFF', '锚点2': '#FFA39E'} |
76 |
77 |
78 | lineOffsetY |
79 | number |
80 | 0 |
81 | 线条起始点向上偏移量 |
82 |
83 |
84 | startPointAnchorId |
85 | string |
86 | |
87 | 保持所有线条起始点与 startPointAnchorId 线条一致 |
88 |
89 |
90 | anchorPlacement |
91 | string |
92 | |
93 | 锚点位置,若值为 bottom,则位于锚点的父节点底部 |
94 |
95 |
96 | selectionAutoScroll |
97 | boolean |
98 | false |
99 | 是否开启框选至边缘时画布自动滚动 |
100 |
101 |
102 | lineTextMap |
103 | object |
104 | {} |
105 | 线条上文字与 anchorId 映射对象 eg: {'anchorId1': '锚点1', 'anchorId2': '锚点2'} |
106 |
107 |
108 | lineTextDecorator |
109 | (text: textPosition, line: ITopologyLine) |
110 | - |
111 | 线条上文字装饰器 |
112 |
113 |
114 | showText |
115 | (start: string) => boolean |
116 | - |
117 | 当 anchorId 为 startPointAnchorId 时, 是否显示线条文字 |
118 |
119 |
120 | showBar |
121 | bool |
122 | true |
123 | 是否显示工具栏 |
124 |
125 |
126 | showCenter |
127 | bool |
128 | true |
129 | 是否显示工具栏中的定位中心 |
130 |
131 |
132 | showLayout |
133 | bool |
134 | true |
135 | 是否显示工具栏中的自动布局 |
136 |
137 |
138 | showDownload |
139 | bool |
140 | false |
141 | 是否显示工具栏中的下载图片 |
142 |
143 |
144 | showMinimap |
145 | bool |
146 | false |
147 | 是否显示小地图 |
148 |
149 |
150 | allowNodeInsertOnEdge |
151 | boolean |
152 | |
153 | 是否开启拖拽节点到线中间进行节点插入 |
154 |
155 |
156 | canConnectMultiLines |
157 | bool |
158 | false |
159 | 控制一个锚点是否可以连接多条线 |
160 |
161 |
162 | overlap |
163 | bool |
164 | false |
165 | 是否允许节点覆盖,默认允许,设置 true 时不允许 |
166 |
167 |
168 | overlapCallback |
169 | () => void |
170 | |
171 | overlap 为 true 时的回调 |
172 |
173 |
174 | overlapOffset |
175 | {} |
176 | {offsetX: 0, offsetY: 0} |
177 | overlap 为 true 时,节点的 x,y 偏移量 |
178 |
179 |
180 | customPostionHeight |
181 | number |
182 | 0 |
183 | 未设置 customPostionHeight 画布默认居中展示,当设置 customPostionHeight 时,画布距离顶部 customPostionHeight |
184 |
185 |
186 | readOnly |
187 | bool |
188 | false |
189 | 只读模式,为true时不可编辑 |
190 |
191 |
192 | prevNodeStyle |
193 | object |
194 | - |
195 | 控制预览节点样式,目前支持配置 border、background 属性 |
196 |
197 |
198 | isReduceRender |
199 | bool |
200 | false |
201 | 控制节点 shouldComponentUpdate 生命周期的返回值 |
202 |
203 |
204 | autoLayout |
205 | bool |
206 | false |
207 | 自动布局,当数据中没有position属性时将自动计算布局。 |
208 |
209 |
210 | renderTreeNode |
211 | (node,decorators) => ReactNode |
212 | - |
213 | 子节点render方法,接收节点数据,返回JSX。 |
214 |
215 |
216 | getInstance |
217 | (instance: Topology) => void |
218 | - |
219 | 返回组件实例,用于调用组件内部的方法。 |
220 |
221 |
222 | onChange |
223 | (data, changeType) => void |
224 | - |
225 | 数据发成改变时触发,changeType为改变的类型 |
226 |
227 |
228 | onSelect |
229 | (data) => void |
230 | - |
231 | 选中数据时触发,返回当前选中的数据(包含节点、线段) |
232 |
233 |
234 | sortChildren |
235 | (parent, children) => sortedChildren |
236 | - |
237 | 子节点排序回调,可选,默认无。 |
238 |
239 |
240 |
241 |
242 | node options 中 一些可配置参数
243 |
244 |
245 |
246 | name |
247 | type |
248 | default |
249 | description |
250 |
251 |
252 |
253 | id |
254 | string |
255 | - |
256 | 节点id |
257 |
258 |
259 | canDrag |
260 | boolean |
261 | true |
262 | 控制节点是否可拖拽 |
263 |
264 |
265 | dragChild |
266 | boolean |
267 | false |
268 | 设置当前节点下的子节点是否需要联动拖动 |
269 |
270 |
271 | filterOverlap |
272 | boolean |
273 | false |
274 | 控制节点在设置 overlap 为 true 时是否仍允许被覆盖 |
275 |
276 |
277 |
278 | ### getInstance
279 |
280 | 返回topology组件的实例,可通过实例调用组件内部的方法:
281 |
282 | #### scrollCanvasToCenter():void
283 |
284 | 移动到中心,当所有节点都有位置数据(positions)时,移动的中心点为内容的中心,否则为画布的中心。
285 |
286 | #### autoLayout():void
287 |
288 | 自动计算布局
289 |
290 | ### decorators
291 |
292 | renderTreeNode的第二个参数,包含以下装饰器函数:
293 |
294 | #### anchorDecorator
295 |
296 | anchorDecorator是一个高阶函数,经过 anchorDecorator 包装的控件将变成一个锚点。
297 |
298 | ##### 用法
299 |
300 | ```javascript
301 | anchorDecorator(options)(ReactNode)
302 | ```
303 |
304 | ##### options参数
305 |
306 |
307 |
308 | name |
309 | type |
310 | default |
311 | description |
312 |
313 |
314 |
315 |
316 | anchorId |
317 | string |
318 | - |
319 | 锚点唯一id,如果不传将默认生成一个自增的id |
320 |
321 |
322 |
323 |
324 | ### TemplateWrapper
325 |
326 | 模板装饰器,用于包装模板组件
327 |
328 | #### 用法
329 | disabled 字段控制 TemplateNode 是否启用
330 | ```javascript
331 |
332 | 模板节点
333 |
334 |
335 | ```
336 |
337 | #### props
338 |
339 |
340 |
341 |
342 | name |
343 | type |
344 | default |
345 | description |
346 |
347 |
348 |
349 |
350 | data |
351 | () => nodeData |
352 | - |
353 | 数据生成器,用于产生节点数据 |
354 |
355 |
356 |
357 |
--------------------------------------------------------------------------------
/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "useBuiltIns": "entry",
7 | "corejs": "3.22"
8 | }
9 | ],
10 | "@babel/preset-typescript"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["@commitlint/config-conventional"]
3 | };
4 |
--------------------------------------------------------------------------------
/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 | const NODE_ENV = process.env.NODE_ENV;
11 | if (!NODE_ENV) {
12 | throw new Error(
13 | 'The NODE_ENV environment variable is required but was not specified.'
14 | );
15 | }
16 |
17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18 | var dotenvFiles = [
19 | `${paths.dotenv}.${NODE_ENV}.local`,
20 | `${paths.dotenv}.${NODE_ENV}`,
21 | // Don't include `.env.local` for `test` environment
22 | // since normally you expect tests to produce the same
23 | // results for everyone
24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25 | paths.dotenv,
26 | ].filter(Boolean);
27 |
28 | // Load environment variables from .env* files. Suppress warnings using silent
29 | // if this file is missing. dotenv will never modify any environment variables
30 | // that have already been set. Variable expansion is supported in .env files.
31 | // https://github.com/motdotla/dotenv
32 | // https://github.com/motdotla/dotenv-expand
33 | dotenvFiles.forEach(dotenvFile => {
34 | if (fs.existsSync(dotenvFile)) {
35 | require('dotenv-expand')(
36 | require('dotenv').config({
37 | path: dotenvFile,
38 | })
39 | );
40 | }
41 | });
42 |
43 | // We support resolving modules according to `NODE_PATH`.
44 | // This lets you use absolute paths in imports inside large monorepos:
45 | // https://github.com/facebook/create-react-app/issues/253.
46 | // It works similar to `NODE_PATH` in Node itself:
47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
50 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
51 | // We also resolve them to make sure all tools using them work consistently.
52 | const appDirectory = fs.realpathSync(process.cwd());
53 | process.env.NODE_PATH = (process.env.NODE_PATH || '')
54 | .split(path.delimiter)
55 | .filter(folder => folder && !path.isAbsolute(folder))
56 | .map(folder => path.resolve(appDirectory, folder))
57 | .join(path.delimiter);
58 |
59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60 | // injected into the application via DefinePlugin in Webpack configuration.
61 | const REACT_APP = /^REACT_APP_/i;
62 |
63 | function getClientEnvironment(publicUrl) {
64 | const raw = Object.keys(process.env)
65 | .filter(key => REACT_APP.test(key))
66 | .reduce(
67 | (env, key) => {
68 | env[key] = process.env[key];
69 | return env;
70 | },
71 | {
72 | // Useful for determining whether we’re running in production mode.
73 | // Most importantly, it switches React into the correct mode.
74 | NODE_ENV: process.env.NODE_ENV || 'development',
75 | // Useful for resolving the correct path to static assets in `public`.
76 | // For example,
.
77 | // This should only be used as an escape hatch. Normally you would put
78 | // images into the `src` and `import` them in code to get their paths.
79 | PUBLIC_URL: publicUrl,
80 | }
81 | );
82 | // Stringify all values so we can feed into Webpack DefinePlugin
83 | const stringified = {
84 | 'process.env': Object.keys(raw).reduce((env, key) => {
85 | env[key] = JSON.stringify(raw[key]);
86 | return env;
87 | }, {}),
88 | };
89 |
90 | return { raw, stringified };
91 | }
92 |
93 | module.exports = getClientEnvironment;
94 |
--------------------------------------------------------------------------------
/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 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | const assetFilename = JSON.stringify(path.basename(filename));
11 |
12 | if (filename.match(/\.svg$/)) {
13 | return `module.exports = {
14 | __esModule: true,
15 | default: ${assetFilename},
16 | ReactComponent: (props) => ({
17 | $$typeof: Symbol.for('react.element'),
18 | type: 'svg',
19 | ref: null,
20 | key: null,
21 | props: Object.assign({}, props, {
22 | children: ${assetFilename}
23 | })
24 | }),
25 | };`;
26 | }
27 |
28 | return `module.exports = ${assetFilename};`;
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/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