├── .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 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 |
nametypedefaultdescription
dataobject{ nodes: []; lines: [] }数据
snaplinebooltrue是否显示辅助对齐线
lineLinkageHighlightbooleanfalsehover 节点线条是否联动高亮
lineColorobject{}线条颜色映射对象 eg: {'锚点1': '#82BEFF', '锚点2': '#FFA39E'}
lineTextColorobject{}线条上文字颜色映射对象 eg: {'锚点1': '#82BEFF', '锚点2': '#FFA39E'}
lineOffsetYnumber0线条起始点向上偏移量
startPointAnchorIdstring保持所有线条起始点与 startPointAnchorId 线条一致
anchorPlacementstring锚点位置,若值为 bottom,则位于锚点的父节点底部
selectionAutoScrollbooleanfalse是否开启框选至边缘时画布自动滚动
lineTextMapobject{}线条上文字与 anchorId 映射对象 eg: {'anchorId1': '锚点1', 'anchorId2': '锚点2'}
lineTextDecorator(text: textPosition, line: ITopologyLine)-线条上文字装饰器
showText(start: string) => boolean-当 anchorId 为 startPointAnchorId 时, 是否显示线条文字
showBarbooltrue是否显示工具栏
showCenterbooltrue是否显示工具栏中的定位中心
showLayoutbooltrue是否显示工具栏中的自动布局
showDownloadboolfalse是否显示工具栏中的下载图片
showMinimapboolfalse是否显示小地图
allowNodeInsertOnEdgeboolean是否开启拖拽节点到线中间进行节点插入
canConnectMultiLinesboolfalse控制一个锚点是否可以连接多条线
overlapboolfalse是否允许节点覆盖,默认允许,设置 true 时不允许
overlapCallback() => voidoverlap 为 true 时的回调
overlapOffset{}{offsetX: 0, offsetY: 0}overlap 为 true 时,节点的 x,y 偏移量
customPostionHeightnumber0未设置 customPostionHeight 画布默认居中展示,当设置 customPostionHeight 时,画布距离顶部 customPostionHeight
readOnlyboolfalse只读模式,为true时不可编辑
prevNodeStyleobject-控制预览节点样式,目前支持配置 border、background 属性
isReduceRenderboolfalse控制节点 shouldComponentUpdate 生命周期的返回值
autoLayoutboolfalse自动布局,当数据中没有position属性时将自动计算布局。
renderTreeNode(node,decorators) => ReactNode-子节点render方法,接收节点数据,返回JSX。
getInstance(instance: Topology) => void-返回组件实例,用于调用组件内部的方法。
onChange(data, changeType) => void-数据发成改变时触发,changeType为改变的类型
onSelect(data) => void-选中数据时触发,返回当前选中的数据(包含节点、线段)
sortChildren(parent, children) => sortedChildren-子节点排序回调,可选,默认无。
241 | 242 | node options 中 一些可配置参数 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 |
nametypedefaultdescription
idstring-节点id
canDragbooleantrue控制节点是否可拖拽
dragChildbooleanfalse设置当前节点下的子节点是否需要联动拖动
filterOverlapbooleanfalse控制节点在设置 overlap 为 true 时是否仍允许被覆盖
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 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 |
nametypedefaultdescription
anchorIdstring-锚点唯一id,如果不传将默认生成一个自增的id
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 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 |
nametypedefaultdescription
data() => nodeData-数据生成器,用于产生节点数据
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