├── .commitlintrc.js
├── .env
├── .env.ra.dev
├── .env.ra.production
├── .env.ra.starandsea
├── .eslintrc
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── config
├── env.js
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── modules.js
├── paths.js
├── pnpTs.js
├── webpack.config.js
└── webpackDevServer.config.js
├── package.json
├── public
├── assets
│ └── less.min.js
├── favicon.ico
├── images
│ └── icons
│ │ ├── icon-128x128.png
│ │ ├── icon-144x144.png
│ │ ├── icon-152x152.png
│ │ ├── icon-192x192.png
│ │ ├── icon-384x384.png
│ │ ├── icon-512x512.png
│ │ ├── icon-72x72.png
│ │ └── icon-96x96.png
├── index.html
├── logo.png
├── manifest.json
├── robots.txt
└── theme.less
├── screenshots
├── logo.png
├── menu_draggable.gif
├── pwa.png
└── themepicker.png
├── scripts
├── build.js
├── start.js
└── test.js
├── src
├── App.test.tsx
├── App.tsx
├── Page.tsx
├── components
│ ├── HeaderCustom.tsx
│ ├── Page.tsx
│ ├── SiderCustom.tsx
│ ├── SiderMenu.tsx
│ ├── animation
│ │ ├── BasicAnimations.tsx
│ │ └── ExampleAnimations.tsx
│ ├── auth
│ │ ├── Basic.tsx
│ │ └── RouterEnter.tsx
│ ├── charts
│ │ ├── Echarts.tsx
│ │ ├── EchartsArea.tsx
│ │ ├── EchartsEffectScatter.tsx
│ │ ├── EchartsForce.tsx
│ │ ├── EchartsGraphnpm.tsx
│ │ ├── EchartsPie.tsx
│ │ ├── EchartsScatter.tsx
│ │ ├── Recharts.tsx
│ │ ├── RechartsBarChart.tsx
│ │ ├── RechartsRadarChart.tsx
│ │ ├── RechartsRadialBarChart.tsx
│ │ └── RechartsSimpleLineChart.tsx
│ ├── cssmodule
│ │ ├── index.module.less
│ │ └── index.tsx
│ ├── dashboard
│ │ ├── Dashboard.tsx
│ │ ├── EchartsProjects.tsx
│ │ └── EchartsViews.tsx
│ ├── env
│ │ └── index.tsx
│ ├── extension
│ │ ├── MultipleMenu.tsx
│ │ ├── QueryParams.tsx
│ │ └── Visitor.tsx
│ ├── index.tsx
│ ├── pages
│ │ ├── Login.tsx
│ │ └── NotFound.tsx
│ ├── smenu
│ │ ├── Sub1.tsx
│ │ └── Sub2.tsx
│ ├── tables
│ │ ├── AdvancedTables.tsx
│ │ ├── AsynchronousTable.tsx
│ │ ├── BasicTable.tsx
│ │ ├── BasicTables.tsx
│ │ ├── ExpandedTable.tsx
│ │ ├── FixedTable.tsx
│ │ ├── SearchTable.tsx
│ │ ├── SelectTable.tsx
│ │ └── SortTable.tsx
│ ├── ui
│ │ ├── Buttons.tsx
│ │ ├── Draggable.tsx
│ │ ├── Gallery.tsx
│ │ ├── Icons.tsx
│ │ ├── Modals.tsx
│ │ ├── Notifications.tsx
│ │ ├── Spins.tsx
│ │ ├── Tabs.tsx
│ │ ├── Wysiwyg.tsx
│ │ ├── banners
│ │ │ ├── AutoPlay.tsx
│ │ │ ├── Basic.tsx
│ │ │ ├── Custom.tsx
│ │ │ └── index.tsx
│ │ ├── emoji
│ │ │ ├── iconfont.ts
│ │ │ └── index.tsx
│ │ └── map
│ │ │ ├── Tencent.tsx
│ │ │ └── index.tsx
│ └── widget
│ │ ├── AuthWidget.tsx
│ │ ├── BreadcrumbCustom.tsx
│ │ ├── Copyright.tsx
│ │ ├── Loading.tsx
│ │ ├── PwaInstaller.tsx
│ │ ├── ThemePicker.tsx
│ │ └── index.tsx
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── routes
│ ├── RouteWrapper.tsx
│ ├── config.ts
│ └── index.tsx
├── service
│ ├── config.ts
│ ├── index.ts
│ └── tools.ts
├── serviceWorker.ts
├── style
│ ├── antd
│ │ ├── header.less
│ │ ├── index.less
│ │ ├── layout.less
│ │ ├── menu.less
│ │ ├── reset.less
│ │ ├── utils.less
│ │ └── variables.less
│ ├── app.less
│ ├── banner.less
│ ├── button.less
│ ├── card.less
│ ├── font
│ │ └── y6oxFxU60dYw9khW6q8jGw.woff2
│ ├── global.less
│ ├── icons.less
│ ├── img.less
│ ├── imgs
│ │ ├── 404.png
│ │ ├── b1.jpg
│ │ ├── beauty.jpg
│ │ ├── installer.png
│ │ ├── logo.png
│ │ ├── mobile.gif
│ │ └── spot_location.png
│ ├── index.less
│ ├── lib
│ │ └── animate.css
│ ├── login.less
│ ├── menu.less
│ ├── modal.less
│ ├── scroll.less
│ ├── table.less
│ ├── theme
│ │ ├── index.js
│ │ ├── theme-danger.json
│ │ ├── theme-grey.json
│ │ ├── theme-info.json
│ │ └── theme-warn.json
│ ├── utils-border.less
│ ├── utils-color.less
│ ├── utils-size.less
│ ├── utils-spacing.less
│ ├── utils-text.less
│ └── variables.less
└── utils
│ ├── hooks.ts
│ └── index.ts
├── theme.js
├── tsconfig.json
└── yarn.lock
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | rules: {},
4 | };
5 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | PORT=3006
--------------------------------------------------------------------------------
/.env.ra.dev:
--------------------------------------------------------------------------------
1 | REACT_ADMIN_ENV=我是本地开发环境配置
--------------------------------------------------------------------------------
/.env.ra.production:
--------------------------------------------------------------------------------
1 | REACT_ADMIN_ENV=我是打包线上环境配置
2 | REACT_ADMIN_TEST=我是其他环境配置
--------------------------------------------------------------------------------
/.env.ra.starandsea:
--------------------------------------------------------------------------------
1 | REACT_ADMIN_ENV=我是星辰大海,你运行yarn starandsea试试
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "plugins": [
4 | "react-hooks"
5 | ],
6 | "rules": {
7 | "react-hooks/rules-of-hooks": "error",
8 | "react-hooks/exhaustive-deps": "warn",
9 | "no-multi-spaces": 1,
10 | "react/jsx-tag-spacing": 1, // 总是在自动关闭的标签前加一个空格,正常情况下也不需要换行
11 | "jsx-quotes": 1,
12 | "react/jsx-closing-bracket-location": 1, // 遵循JSX语法缩进/格式
13 | "react/jsx-boolean-value": 1, // 如果属性值为 true, 可以直接省略
14 | "react/no-string-refs": 1, // 总是在Refs里使用回调函数
15 | "react/self-closing-comp": 1, // 对于没有子元素的标签来说总是自己关闭标签
16 | "react/sort-comp": 1, // 按照具体规范的React.createClass 的生命周期函数书写代码
17 | "react/jsx-pascal-case": 1 // React模块名使用帕斯卡命名,实例使用骆驼式命名
18 | }
19 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | public/theme.less
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 4,
4 | "singleQuote": true,
5 | "jsxBracketSameLine": false,
6 | "printWidth": 100
7 | }
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | script:
5 | - yarn
6 | - yarn build
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 更新日志
2 |
3 | #### 2017-07-08
4 |
5 | - 依赖包版本升级
6 | - react@15.6.1
7 | - antd@2.11.2
8 | - webpack@2.6.1
9 | - 等等
10 |
11 | #### 2017-08-01
12 |
13 | - 引入 redux 系列
14 | - redux@3.7.2
15 | - redux-thunk@2.2.0
16 | - react-redux@5.0.5
17 | - 增加权限管理模块
18 | - 使用 easy-mock 模拟数据模拟登录接口
19 | - 使用 redux 系列将登录用户数据传递给权限组件
20 | - 权限组件采用 Render Callback 的方式传递权限给需要受控制的组件(具体做法请查看源代码。)
21 | - 用户状态保存在 localStorage 中
22 | - 具体做法请运行项目查看
23 | - PS:以上管理权限只是一种方式,但这绝对不是唯一的方式,也不是最好的方式。如果你有更好的方式,不妨加上面的群和大家一起分享下。😄😄
24 | - 增加路径别名
25 | - 使用@别名处理引入组件相对路径过长问题。
26 | - 缺点:编辑器不能使用快捷提示和快捷跳转到相应的文件
27 |
28 | #### 2017-08-13
29 |
30 | - 权限管理模块增加页面跳转权限验证
31 | - 点击权限管理的路由拦截,若没有访问权限则会跳转到 404 页面。
32 | - 大致实现方式(非常简单):通过向自定义 router 组件传入 store,登录之后可获取到 redux 中的权限 state 数据,并通过判断是否包含权限进行跳转。ps: 该 demo 的效果是管理员登录之后才能跳转到路由拦截页面。具体操作请拉取代码尝试。
33 |
34 | #### 2017-08-26
35 |
36 | - 增加响应式布局 - 替换 antd Col 组件的响应式栅格为 md(具体参数用法请查看 antd 官方文档) - 初始化页面是获取当前浏览器宽度设置菜单显示类型 - 监听 window 的 onresize 函数,设置菜单显示类型。PS:浏览器宽度存入 redux 中,方便组件之间传递。
37 | 
38 |
39 | #### 2017-09-13
40 |
41 | - 依赖包版本升级
42 | - antd@2.13.1(目前最新版)
43 |
44 | #### 2017-10-21
45 |
46 | - 开发环境增加 react-hot-loader-保持状态刷新组件(译:实时调整组件),可参考以下相关项目
47 | - [react-hot-loader](https://github.com/gaearon/react-hot-loader)
48 |
49 | #### 2017-12-12
50 |
51 | - 依赖包版本升级
52 | - antd@3.0.1(目前最新版)
53 | - react-router-dom@4.2.2
54 | - 大改动
55 | - react-router 切换 4.x 版本,切换响应的版本路由写法(具体见代码更新日志)
56 | - ps: react-router 3.x 的版本请查看代码分支 router3.x
57 |
58 | #### 2018-01-12
59 |
60 | - 增加 cssmodule 的支持(css, less)
61 |
62 | - 建议用 css 预处理器,文件名为 xxx.module.less,引入相应组件即可使用。
63 |
64 | - 具体做法参见新增模块,路由后缀:/app/cssModule。[点击访问](http://cheng_haohao.oschina.io/reactadmin/#/app/cssModule)
65 |
66 | #### 2018-10-13
67 |
68 | - 重大更新 :sparkles:
69 | - 升级 create-react-app 2.x,详情文档见[官方文档](https://reactjs.org/blog/2018/10/01/create-react-app-v2.html)
70 | - 升级大部分第三方库,升级版本见[commit](https://github.com/yezihaohao/react-admin/commit/d8dc0ff0c6517c57a46d731adba69112a55145a9#diff-b9cfc7f2cdf78a7f4b91a753d10865a2)
71 | - 增加自定义主题功能 - 主题基础样式配置见[variables.less](https://github.com/yezihaohao/react-admin/blob/master/src/style/antd/variables.less) - 修改主题基础样式后执行`yarn theme 或 npm run theme`,默认主题即可生效 - 页面上可自定义主题颜色配置(根据此可添加字体大小等其他 antd 的默认样式)
72 | 
73 |
74 | #### 2018-11-07
75 |
76 | - 完善 PWA 的 manifest.json 文件,增加按钮手动触发安装 PWA 应用
77 | - 最新版的 chrome 浏览器访问[ReactAdmin](https://admiring-dijkstra-34cb29.netlify.com/)即可体验
78 |
79 | 
80 |
81 | #### 2018-11-26
82 |
83 | - 增加问号形式的路由参数扩展
84 |
85 | #### 2018-12-28
86 |
87 | - 增加[react-document-title](https://github.com/gaearon/react-document-title)组件,根据路由设置页面 title
88 |
89 | #### 2019-03-20
90 |
91 | - 增加[redux-alita](https://github.com/yezihaohao/redux-alita),极简的 redux 工具用法,详情见其代码仓库
92 |
93 | #### 2019-05-10
94 |
95 | - 升级 react,react-dom,增加 hooks 支持(去掉 react-hot-loader,老版本 hot-loader 使用 hook 有点问题)
96 | - 增加菜单可拖拽
97 |
98 | 
99 |
100 | #### 2019-09-04
101 |
102 | - 增加 Git 提交 message 规范约束工具[commitizen](https://github.com/commitizen/cz-cli)
103 | - Git 提交规范往往是团队编码必需,借助工具能形成更好的约束,如果你不喜欢用,可参照提交记录去掉[bd426fd](https://github.com/yezihaohao/react-admin/commit/a9401d191edd077bc3e59c8dbeeb61e5029cde95)
104 |
105 | #### 2019-09-26
106 |
107 | - 更新 create-react-app3.x 版本,升级部分依赖 lib,详情请查看提交记录(有问题请提 issue)
108 |
109 | #### 2019-10-26
110 |
111 | - 新增访客模式的路由配置+demo(主路由配置)
112 | - [在线 Demo](https://admiring-dijkstra-34cb29.netlify.com/#/app/extension/visitor)
113 |
114 | #### 2019-12-18
115 |
116 | - 新增多级菜单配置功能(菜单可配置成无限的树状菜单,菜单嵌套过多时,样式问题可能需要你调整)
117 |
118 | #### 2020-01-21
119 |
120 | - 新增服务端异步菜单功能
121 |
122 | #### 2020-08-02
123 |
124 | - 新增多环境配置方案,环境配置任你加,源码详情请查看[提交记录](https://github.com/yezihaohao/react-admin/commit/d2cb53dca7e7179c794dc9e699d057ed549aec62)
125 | - 根目录增加 .env.ra.xxx,其中 xxx 是 package.json 中运行的脚本命令第三个参数,请结合项目查看
126 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 yezihaohao
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 |
--------------------------------------------------------------------------------
/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 | const REACT_ADMIN = process.env.REACT_ADMIN;
12 | if (!NODE_ENV) {
13 | throw new Error('The NODE_ENV environment variable is required but was not specified.');
14 | }
15 |
16 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
17 | const dotenvFiles = [
18 | `${paths.dotenv}.${NODE_ENV}.local`,
19 | `${paths.dotenv}.${NODE_ENV}`,
20 | `${paths.dotenv}.ra.${REACT_ADMIN}`,
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 | const REACT_ADMIN_REG = /^REACT_ADMIN_/i;
63 |
64 | function getClientEnvironment(publicUrl) {
65 | const raw = Object.keys(process.env)
66 | .filter((key) => REACT_APP.test(key) || REACT_ADMIN_REG.test(key))
67 | .reduce(
68 | (env, key) => {
69 | env[key] = process.env[key];
70 | return env;
71 | },
72 | {
73 | // Useful for determining whether we’re running in production mode.
74 | // Most importantly, it switches React into the correct mode.
75 | NODE_ENV: process.env.NODE_ENV || 'development',
76 | // Useful for resolving the correct path to static assets in `public`.
77 | // For example,
.
78 | // This should only be used as an escape hatch. Normally you would put
79 | // images into the `src` and `import` them in code to get their paths.
80 | PUBLIC_URL: publicUrl,
81 | }
82 | );
83 | // Stringify all values so we can feed into Webpack DefinePlugin
84 | const stringified = {
85 | 'process.env': Object.keys(raw).reduce((env, key) => {
86 | env[key] = JSON.stringify(raw[key]);
87 | return env;
88 | }, {}),
89 | };
90 |
91 | return { raw, stringified };
92 | }
93 |
94 | module.exports = getClientEnvironment;
95 |
--------------------------------------------------------------------------------
/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/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 | // We need to explicitly check for null and undefined (and not a falsy value) because
18 | // TypeScript treats an empty string as `.`.
19 | if (baseUrl == null) {
20 | // If there's no baseUrl set we respect NODE_PATH
21 | // Note that NODE_PATH is deprecated and will be removed
22 | // in the next major release of create-react-app.
23 |
24 | const nodePath = process.env.NODE_PATH || '';
25 | return nodePath.split(path.delimiter).filter(Boolean);
26 | }
27 |
28 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
29 |
30 | // We don't need to do anything if `baseUrl` is set to `node_modules`. This is
31 | // the default behavior.
32 | if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
33 | return null;
34 | }
35 |
36 | // Allow the user set the `baseUrl` to `appSrc`.
37 | if (path.relative(paths.appSrc, baseUrlResolved) === '') {
38 | return [paths.appSrc];
39 | }
40 |
41 | // If the path is equal to the root directory we ignore it here.
42 | // We don't want to allow importing from the root directly as source files are
43 | // not transpiled outside of `src`. We do allow importing them with the
44 | // absolute path (e.g. `src/Components/Button.js`) but we set that up with
45 | // an alias.
46 | if (path.relative(paths.appPath, baseUrlResolved) === '') {
47 | return null;
48 | }
49 |
50 | // Otherwise, throw an error.
51 | throw new Error(
52 | chalk.red.bold(
53 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." +
54 | ' Create React App does not support other values at this time.'
55 | )
56 | );
57 | }
58 |
59 | /**
60 | * Get webpack aliases based on the baseUrl of a compilerOptions object.
61 | *
62 | * @param {*} options
63 | */
64 | function getWebpackAliases(options = {}) {
65 | const baseUrl = options.baseUrl;
66 |
67 | if (!baseUrl) {
68 | return {};
69 | }
70 |
71 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
72 |
73 | if (path.relative(paths.appPath, baseUrlResolved) === '') {
74 | return {
75 | src: paths.appSrc,
76 | };
77 | }
78 | }
79 |
80 | /**
81 | * Get jest aliases based on the baseUrl of a compilerOptions object.
82 | *
83 | * @param {*} options
84 | */
85 | function getJestAliases(options = {}) {
86 | const baseUrl = options.baseUrl;
87 |
88 | if (!baseUrl) {
89 | return {};
90 | }
91 |
92 | const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
93 |
94 | if (path.relative(paths.appPath, baseUrlResolved) === '') {
95 | return {
96 | 'src/(.*)$': '/src/$1',
97 | };
98 | }
99 | }
100 |
101 | function getModules() {
102 | // Check if TypeScript is setup
103 | const hasTsConfig = fs.existsSync(paths.appTsConfig);
104 | const hasJsConfig = fs.existsSync(paths.appJsConfig);
105 |
106 | if (hasTsConfig && hasJsConfig) {
107 | throw new Error(
108 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
109 | );
110 | }
111 |
112 | let config;
113 |
114 | // If there's a tsconfig.json we assume it's a
115 | // TypeScript project and set up the config
116 | // based on tsconfig.json
117 | if (hasTsConfig) {
118 | const ts = require(resolve.sync('typescript', {
119 | basedir: paths.appNodeModules,
120 | }));
121 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
122 | // Otherwise we'll check if there is jsconfig.json
123 | // for non TS projects.
124 | } else if (hasJsConfig) {
125 | config = require(paths.appJsConfig);
126 | }
127 |
128 | config = config || {};
129 | const options = config.compilerOptions || {};
130 |
131 | const additionalModulePaths = getAdditionalModulePaths(options);
132 |
133 | return {
134 | additionalModulePaths: additionalModulePaths,
135 | webpackAliases: getWebpackAliases(options),
136 | jestAliases: getJestAliases(options),
137 | hasTsConfig,
138 | };
139 | }
140 |
141 | module.exports = getModules();
142 |
--------------------------------------------------------------------------------
/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
28 |
35 |
38 |
39 |