├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── modules.js ├── paths.js ├── pnpTs.js ├── webpack.config.js └── webpackDevServer.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── icons │ └── your-icon.png ├── index.html ├── static │ └── img │ │ └── logo.png └── tinymce │ ├── langs │ └── zh_CN.js │ └── skins │ ├── content │ ├── default │ │ ├── content.css │ │ └── content.min.css │ ├── document │ │ ├── content.css │ │ └── content.min.css │ └── writer │ │ ├── content.css │ │ └── content.min.css │ └── ui │ ├── oxide-dark │ ├── content.css │ ├── content.inline.css │ ├── content.inline.min.css │ ├── content.min.css │ ├── content.mobile.css │ ├── content.mobile.min.css │ ├── skin.css │ ├── skin.min.css │ ├── skin.mobile.css │ ├── skin.mobile.min.css │ └── tinymce-mobile.woff │ └── oxide │ ├── content.css │ ├── content.inline.css │ ├── content.inline.min.css │ ├── content.min.css │ ├── content.mobile.css │ ├── content.mobile.min.css │ ├── fonts │ └── tinymce-mobile.woff │ ├── skin.css │ ├── skin.min.css │ ├── skin.mobile.css │ ├── skin.mobile.min.css │ └── tinymce-mobile.woff ├── scripts ├── build.js ├── start.js └── test.js ├── server.js ├── src ├── App.tsx ├── app.scss ├── assets │ ├── img │ │ ├── about │ │ │ ├── avatar.png │ │ │ ├── header-bg.png │ │ │ ├── icon.png │ │ │ ├── icon1.png │ │ │ ├── icon2.png │ │ │ ├── icon3.png │ │ │ ├── icon4.png │ │ │ ├── open-source.jpg │ │ │ ├── qrcode.jpg │ │ │ ├── team-icon.png │ │ │ └── welcome.png │ │ ├── error-page │ │ │ ├── 404.png │ │ │ └── logo.png │ │ ├── login │ │ │ ├── login-ba.png │ │ │ ├── login-btn.png │ │ │ ├── nickname.png │ │ │ ├── password.png │ │ │ └── team-name.png │ │ ├── logo.png │ │ ├── mobile-logo.png │ │ ├── user │ │ │ ├── corner.png │ │ │ ├── user-bg.png │ │ │ ├── user.jpg │ │ │ └── user.png │ │ ├── 小尖角.png │ │ └── 铃铛icon.png │ └── styles │ │ ├── antd │ │ ├── custom-antd.scss │ │ └── modifyVars.js │ │ ├── index.scss │ │ └── realize │ │ ├── animation.scss │ │ ├── lin-variables.scss │ │ └── reset.scss ├── components │ ├── base │ │ ├── cropper │ │ │ └── Cropper.tsx │ │ ├── dynamic-icon │ │ │ ├── DynamicIcon.tsx │ │ │ └── Icons.tsx │ │ ├── lin-checkbox │ │ │ └── LinCheckbox.tsx │ │ ├── lin-header │ │ │ ├── LinHeader.tsx │ │ │ └── lin-header.scss │ │ ├── lin-search │ │ │ ├── LinSearch.tsx │ │ │ └── lin-search.scss │ │ ├── lin-table │ │ │ ├── LinTable.tsx │ │ │ └── lin-table.scss │ │ ├── loading-wrapper │ │ │ ├── LoadingWrapper.tsx │ │ │ └── loading-wrapper.scss │ │ ├── sticky-top │ │ │ ├── StickyTop.tsx │ │ │ └── sticky-top.scss │ │ ├── swiper │ │ │ ├── Swiper.tsx │ │ │ └── swiper.scss │ │ ├── tinymce │ │ │ ├── Tinymce.jsx │ │ │ └── importAll.js │ │ └── upload-imgs │ │ │ ├── UploadImgs.jsx │ │ │ ├── upload-imgs.scss │ │ │ └── utils.js │ └── layout │ │ ├── app-main │ │ ├── AppMain.tsx │ │ └── app-main.scss │ │ ├── breadcrumb │ │ ├── Breadcrumb.tsx │ │ └── breadcrumb.scss │ │ ├── clear-tab │ │ ├── ClearTab.tsx │ │ └── clear-tab.scss │ │ ├── index.ts │ │ ├── menu-tab │ │ ├── MenuTab.tsx │ │ └── menu-tab.scss │ │ ├── nav-bar │ │ ├── NavBar.tsx │ │ └── nav-bar.scss │ │ ├── notify │ │ ├── Notify.tsx │ │ ├── NotifyOverlay.tsx │ │ └── style │ │ │ ├── notify-overlay.scss │ │ │ └── notify.scss │ │ ├── reuse-tab │ │ ├── ReuseTab.tsx │ │ └── reuse-tab.scss │ │ ├── screenfull │ │ ├── Screenfull.tsx │ │ └── screenfull.scss │ │ ├── side-bar │ │ ├── SideBar.tsx │ │ └── side-bar.scss │ │ └── user │ │ ├── Avatar.tsx │ │ ├── PwdForm.tsx │ │ ├── User.tsx │ │ ├── UserBox.tsx │ │ └── style │ │ ├── user-box.scss │ │ └── user.scss ├── config │ ├── error-code.ts │ ├── global.ts │ ├── index.ts │ └── stage │ │ ├── admin.ts │ │ ├── center.ts │ │ ├── index.ts │ │ └── plugins.ts ├── hooks │ ├── base │ │ ├── useAwait.ts │ │ ├── useDeepCompareEffect.ts │ │ ├── useFirstMountState.ts │ │ ├── useWindowScrollY.ts │ │ └── useWindowWidth.ts │ └── project │ │ ├── lib │ │ └── use-websocket │ │ │ ├── attach-listener.ts │ │ │ ├── config.ts │ │ │ ├── create-socket.ts │ │ │ ├── globals.ts │ │ │ ├── proxy.ts │ │ │ ├── types.ts │ │ │ └── use-websocket.ts │ │ └── useWebSocket.ts ├── images.d.ts ├── index.tsx ├── lin │ ├── directives │ │ └── authorize.ts │ ├── models │ │ ├── admin.ts │ │ ├── log.ts │ │ ├── notify.js │ │ └── user.ts │ ├── plugins │ │ └── axios.ts │ └── utils │ │ ├── cookie.js │ │ ├── search.ts │ │ ├── time-formatter.ts │ │ ├── token.ts │ │ └── util.ts ├── plugins │ └── custom │ │ ├── stage-config.ts │ │ └── views │ │ ├── tinymce-demo │ │ ├── TinymceDemo.tsx │ │ └── tinymce-demo.scss │ │ └── upload-imgs-demo │ │ ├── UploadImgsDemo.tsx │ │ └── upload-imgs-demo.scss ├── router │ ├── Routes.tsx │ ├── home-router.ts │ └── login-required.ts ├── store │ ├── config.ts │ ├── getters │ │ └── app.getters.ts │ ├── index.ts │ ├── mutations │ │ ├── app.mutations.ts │ │ └── mutation-types.ts │ └── state.ts ├── types │ ├── model.ts │ ├── project.ts │ └── store.ts └── views │ ├── about │ ├── About.tsx │ └── about.scss │ ├── admin │ ├── group │ │ ├── FormModal.tsx │ │ ├── GroupAdd.tsx │ │ ├── GroupInfo.tsx │ │ ├── GroupList.tsx │ │ └── style │ │ │ ├── group-add.scss │ │ │ ├── group-info.scss │ │ │ └── group-list.scss │ └── user │ │ ├── FormModal.tsx │ │ ├── UserAdd.tsx │ │ ├── UserInfo.tsx │ │ ├── UserList.tsx │ │ └── style │ │ ├── user-add.scss │ │ ├── user-info.scss │ │ └── user-list.scss │ ├── center │ ├── Center.tsx │ └── center.scss │ ├── error-page │ ├── ErrorPage.tsx │ └── error-page.scss │ ├── home │ ├── Home.tsx │ └── home.scss │ ├── log │ ├── Log.tsx │ └── log.scss │ └── login │ ├── Login.tsx │ └── login.scss └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | root: true, 4 | env: { 5 | browser: true, 6 | commonjs: true, 7 | es6: true, 8 | node: true, 9 | }, 10 | plugins: ['@typescript-eslint'], 11 | extends: ['react-app', 'eslint:recommended', 'plugin:prettier/recommended'], 12 | parserOptions: { 13 | project: './tsconfig.json', 14 | ecmaVersion: 6, 15 | sourceType: 'module', 16 | ecmaFeatures: { 17 | modules: true, 18 | }, 19 | }, 20 | rules: { 21 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 22 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 23 | camelcase: 0, // 变量可以用下划线 24 | semi: ['error', 'never'], // 无分号 25 | 'linebreak-style': ['error', 'windows'], 26 | 'no-case-declarations': 0, 27 | 'no-async-promise-executor': 0, 28 | 'no-extra-semi': 0, // 和 prettier 冲突 29 | 'max-len': 0, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=typescript 2 | *.jsx linguist-language=typescript 3 | *.tsx linguist-language=typescript 4 | # *.css linguist-language=typescript -------------------------------------------------------------------------------- /.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 | /backup 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, 3 | singleQuote: true, // 字符串是否使用单引号,默认为false,使用双引号 4 | semi: false, // 行位是否使用分号,默认为true 5 | trailingComma: 'all', // 是否使用尾逗号,有三个可选值"" 6 | jsxSingleQuote: true, 7 | jsxBracketSameLine: false, 8 | parser: 'typescript', 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Xu Linkai 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 | 2 |

3 | 4 | 5 | 6 |

7 | 8 |

9 | 简介 | 快速上手 | 版本日志 10 |

11 | 12 | ![](https://img.shields.io/badge/版本-0.2.0-3963bc.svg) 13 | ![](https://img.shields.io/badge/node-8.11.0+-3963bc.svg) 14 | ![](https://img.shields.io/badge/脚手架-create--react--app-3963bc.svg) 15 | ![](https://img.shields.io/badge/license-MIT-3963bc.svg) 16 | ![](https://img.shields.io/badge/developer-@Bongkai-3963bc.svg) 17 | 18 | ## 预防针 19 | 20 | * 本项目非官方团队出品,仅出于学习、研究目的丰富下官方项目的语言支持。官方团队仓库链接:[点击查看](https://github.com/TaleLin) 21 | * 本项目采取后跟进官方团队功能的形式,即官方团队出什么功能,这边就跟进开发什么功能,开发者不必担心适配问题。 22 | * 在上一点的基础上,我们会尝试加入一些自己的想法并实现。 23 | * 局限于本人水平,有些地方还需重构,已经纳入了计划中,当然也会有我没考虑到的,希望有更多人参与进来一起完善。 24 | 25 | ## 简介 26 | 27 | Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套**内容管理系统框架**。 28 | 29 | Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率。 30 | 31 | Lin-CMS 是一套前后端完整的解决方案,后端部署请移步: 32 | 33 | [lin-cms-spring-boot](https://github.com/TaleLin/lin-cms-spring-boot) 34 | 35 | [lin-cms-koa](https://github.com/TaleLin/lin-cms-koa) 36 | 37 | ### 注意 38 | 39 | **Lin-CMS 是工程类开源项目,不能保证无缝升级** 40 | 41 | ### 官方文档地址 42 | 43 | [http://doc.cms.7yue.pro/](http://doc.cms.7yue.pro/) 44 | 45 | #### 项目后端 PHP 版作者编写的 Vue + PHP 文档地址,可供进一步参考 46 | 47 | [https://chenjinchuang.github.io/](https://chenjinchuang.github.io/) 48 | 49 | ### 线上 Demo 50 | 51 | [http://lincms.bongkai.com/](http://lincms.bongkai.com/) 52 | 53 | 54 | ### Lin CMS 的特点 55 | 56 | Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。 57 | 58 | #### Lin CMS 是一个前后端分离的 CMS 解决方案 59 | 60 | 这意味着,Lin 既提供后台的支撑,也有一套对应的前端系统,当然双端分离的好处不仅仅在于此,我们会在后续提供`NodeJS`和`PHP`版本的 Lin。如果你心仪 Lin,却又因为技术栈的原因无法即可使用,没关系,我们会在后续提供更多的语言版本。为什么 Lin 要选择前后端分离的单页面架构呢? 61 | 62 | 首先,传统的网站开发更多的是采用服务端渲染的方式,需用使用一种模板语言在服务端完成页面渲染:比如 JinJa2、Jade 等。 63 | 服务端渲染的好处在于可以比较好的支持 SEO,但作为内部使用的 CMS 管理系统,SEO 并不重要。 64 | 65 | 但一个不可忽视的事实是,服务器渲染的页面到底是由前端开发者来完成,还是由服务器开发者来完成?其实都不太合适。现在已经没有多少前端开发者是了解这些服务端模板语言的,而服务器开发者本身是不太擅长开发页面的。那还是分开吧,前端用最熟悉的 Vue/React 写 JS 和 CSS,而服务器只关注自己的 API 即可。 66 | 67 | 其次,单页面应用程序的体验本身就要好于传统网站。 68 | 69 | #### 框架本身已内置了 CMS 常用的功能 70 | 71 | Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需要集中精力开发自己的 CMS 业务即可 72 | 73 | #### Lin CMS 本身也是一套开发规范 74 | 75 | Lin CMS 除了内置常见的功能外,还提供了一套开发规范与工具类。换句话说,开发者无需再纠结如何验证参数?如何操作数据库?如何做全局的异常处理?API 的结构如何?前端结构应该如何组织?这些问题 Lin CMS 已经给出了解决方案。当然,如果你不喜欢 Lin 给出的架构,那么自己去实现自己的 CMS 架构也是可以的。但通常情况下,你确实无需再做出架构上的改动,Lin 可以满足绝大多数中小型的 CMS 需求。 76 | 77 | 举例来说,每个 API 都需要校验客户端传递的参数。但校验的方法有很多种,不同的开发者会有不同的构筑方案。但 Lin 提供了一套验证机制,开发者无需再纠结如何校验参数,只需模仿 Lin 的校验方案去写自己的业务即可。 78 | 79 | 还是基于这样的一个原则:**Lin CMS 只需要开发者关注自己的业务开发,它已经内置了很多机制帮助开发者快速开发自己的业务**。 80 | 81 | ## 快速上手 82 | 83 | ```sh 84 | # clone the project 85 | git clone https://github.com/Bongkai/lin-cms-react.git 86 | 87 | # install dependency 88 | npm install or yarn 89 | 90 | # develop 91 | npm start or yarn start 92 | 93 | # build & run build 94 | npm run build 95 | node server.js 96 | ``` 97 | 98 | ## 讨论交流 99 | 微信公众号搜索:林间有风 100 |
101 | 102 | 103 | QQ群搜索:林间有风 或 643205479 104 | 105 | 106 | 107 | ## 版本日志 108 | 109 | 最新版本 `0.2.0` 110 | 111 | ### 0.2.0 112 | 113 | 1. `U` 使用 [dream-redux](https://github.com/Bongkai/dream-redux) 重构 Redux 代码,极大简化 Redux 的使用 114 | 2. `U` 调整 useSelector 的用法,保证代码性能的同时简化 ts 写法 115 | 116 | ### 0.1.1-beta.1 117 | 118 | 1. `U` 升级 antd v4 119 | 2. `U` 使用 Immer 和自定义 hook 优化 Redux 代码,简化使用方法 120 | 3. `U` 修改项目构建配置,优化动态加载功能 121 | 122 | ### 0.1.0-beta.2 123 | 124 | 1. `A` 新增消息中心组件 125 | 2. `A` 新增 useWebSocket 核心代码库 126 | 127 | ### 0.1.0-beta.1 128 | 129 | 1. `A` 新增一个用户可以属于多个分组 130 | 2. `F` 权限相关 auth right 统一替换为 permission 131 | 3. `A` 使用 babel-plugin-react-directives 添加 r-if 指令优化 tsx 代码 132 | 133 | * 核心功能对应 Vue 版 0.3.0 134 | 135 | ### 0.0.1-alpha.2 136 | 137 | 1. `U` 使用 React Hooks 进行项目重构 138 | 2. `A` 使用 TypeScript 优化大部分项目代码 139 | 3. `A` 添加 ESLint 和 Prettier 提升项目代码规范 140 | 141 | * 核心功能对应 Vue 版 0.2.x 142 | 143 | ### 0.0.1-alpha.1 144 | 145 | 1. `A` 初始化内测版 146 | 147 | -------------------------------------------------------------------------------- /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 | const 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 | 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 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 | // Otherwise, throw an error. 42 | throw new Error( 43 | chalk.red.bold( 44 | "Your project's `baseUrl` can only be set to `src` or `node_modules`." + 45 | ' Create React App does not support other values at this time.' 46 | ) 47 | ); 48 | } 49 | 50 | function getModules() { 51 | // Check if TypeScript is setup 52 | const hasTsConfig = fs.existsSync(paths.appTsConfig); 53 | const hasJsConfig = fs.existsSync(paths.appJsConfig); 54 | 55 | if (hasTsConfig && hasJsConfig) { 56 | throw new Error( 57 | 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' 58 | ); 59 | } 60 | 61 | let config; 62 | 63 | // If there's a tsconfig.json we assume it's a 64 | // TypeScript project and set up the config 65 | // based on tsconfig.json 66 | if (hasTsConfig) { 67 | const ts = require(resolve.sync('typescript', { 68 | basedir: paths.appNodeModules, 69 | })); 70 | config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; 71 | // Otherwise we'll check if there is jsconfig.json 72 | // for non TS projects. 73 | } else if (hasJsConfig) { 74 | config = require(paths.appJsConfig); 75 | } 76 | 77 | config = config || {}; 78 | const options = config.compilerOptions || {}; 79 | 80 | const additionalModulePaths = getAdditionalModulePaths(options); 81 | 82 | return { 83 | additionalModulePaths: additionalModulePaths, 84 | hasTsConfig, 85 | }; 86 | } 87 | 88 | module.exports = getModules(); 89 | -------------------------------------------------------------------------------- /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