├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── package.json ├── scripts ├── build-package.js ├── start-app-dev-server.js └── utils │ ├── compile.js │ ├── get-package-alias.js │ ├── get-package-path.js │ ├── get-packages-data.js │ └── get-src-map.js ├── src ├── apps │ └── hello-world │ │ ├── package.json │ │ ├── src │ │ ├── App.jsx │ │ └── index.jsx │ │ └── webpack.config.js └── packages │ ├── typography │ ├── package.json │ ├── src │ │ ├── Text │ │ │ ├── Text.jsx │ │ │ ├── Text.story.jsx │ │ │ └── Text.styles.less │ │ └── index.js │ └── webpack.config.js │ └── ui │ ├── package.json │ ├── src │ ├── Button │ │ ├── Button.jsx │ │ ├── Button.story.jsx │ │ └── Button.styles.less │ └── index.js │ └── webpack.config.js ├── storybook ├── main.js └── start.js ├── webpack ├── .babelrc.js ├── get-app-config.js ├── get-base-paths.js ├── get-package-config.js └── loaders.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const getSrcMap = require('./scripts/utils/get-src-map'); 2 | 3 | module.exports = { 4 | parser: 'babel-eslint', 5 | extends: ['airbnb'], 6 | env: { 7 | browser: true, 8 | node: true, 9 | }, 10 | 11 | rules: { 12 | // some props require dangle 13 | 'no-underscore-dangle': 'off', 14 | 15 | // sometimes it is better 16 | 'no-nested-ternary': 'off', 17 | 18 | // controlled with prettier 19 | 'arrow-parens': 'off', 20 | 'function-paren-newline': 'off', 21 | 22 | // disabled for condition && someFunc() 23 | 'no-unused-expressions': 'off', 24 | 25 | // backend developers like _, no need to transform data all the time 26 | camelcase: 'off', 27 | 28 | // just the formatting issues, prettier does all the job 29 | 'operator-linebreak': 'off', 30 | 'object-curly-newline': 'off', 31 | 'implicit-arrow-linebreak': 'off', 32 | 'spaced-comment': 'off', 33 | 'comma-dangle': [ 34 | 'error', 35 | { 36 | arrays: 'always-multiline', 37 | objects: 'always-multiline', 38 | imports: 'always-multiline', 39 | exports: 'always-multiline', 40 | functions: 'ignore', 41 | }, 42 | ], 43 | 44 | // increase max line length to 100 45 | 'max-len': [ 46 | 'error', 47 | 100, 48 | { 49 | ignoreTrailingComments: true, 50 | ignoreComments: true, 51 | ignoreUrls: true, 52 | ignoreStrings: true, 53 | ignoreRegExpLiterals: true, 54 | ignoreTemplateLiterals: true, 55 | }, 56 | ], 57 | 58 | // these rules are just bullshit 59 | 'import/prefer-default-export': 'off', 60 | 'react/no-danger': 'off', 61 | 'react/state-in-constructor': 'off', 62 | 'react/jsx-props-no-spreading': 'off', 63 | 'react/require-default-props': 'off', 64 | 'react/jsx-one-expression-per-line': 'off', 65 | 'react/destructuring-assignment': 'off', 66 | 'react/sort-comp': 'off', 67 | 'react/forbid-prop-types': 'off', 68 | 'react/jsx-no-bind': 'off', 69 | 'jsx-a11y/control-has-associated-label': 'off', 70 | 71 | // sometimes there is no alternative 72 | 'react/no-array-index-key': 'off', 73 | 74 | // disable dev dependencies for storybook 75 | 'import/no-extraneous-dependencies': [ 76 | 'warn', 77 | { 78 | devDependencies: [ 79 | '**/*.story.jsx', 80 | '**/*.test.jsx', 81 | '**/storybook/*.js', 82 | '**/webpack.config.js', 83 | '**/*.webpack.config.js', 84 | 'webpack/*', 85 | 'scripts/**', 86 | ], 87 | }, 88 | ], 89 | 90 | // rules are broken and provide falsy mistakes 91 | 'jsx-a11y/label-has-for': 'off', 92 | 'jsx-a11y/anchor-is-valid': 'off', 93 | 'react/no-typos': 'off', // https://github.com/yannickcr/eslint-plugin-react/issues/1389 94 | 95 | // it does not spoil anything if used wisely 96 | 'jsx-a11y/no-autofocus': 'off', 97 | }, 98 | 99 | settings: { 100 | 'import/resolver': { 101 | node: {}, 102 | webpack: {}, 103 | alias: { 104 | map: Object.entries(getSrcMap()), 105 | extensions: ['.ts', '.js', '.jsx', '.json'], 106 | }, 107 | }, 108 | }, 109 | }; 110 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | singleQuote: true, 4 | trailingComma: 'es5', 5 | }; 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vitaly Rtishchev 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 | # React Monorepo Starter 2 | 3 | This project includes bare minimum configuration that you can use to work with react in monorepo. It includes workflow to work both with packages and apps (without ssr). All packages included by default are used for testing and showcasing, you can remove them safely after you finish playing around. 4 | 5 | ## Scripts 6 | 7 | - **npm start** script: 8 | - `npm start @application/name` – start any application 9 | - `npm start @application/name -- --port 3000` – start application on port 3000 10 | - **Storybook** script: 11 | - `npm run storybook` – start storybook with all packages and apps 12 | - `npm run storybook @package/name` – start storybook with specific package 13 | - `npm run storybook @package/first @package/second` – start storybook with specified list of packages 14 | - **Build** script: 15 | - `npm run build @package/name` – build single package 16 | - `npm run build @package/first @package/second` – build list of packages 17 | - **Syncpack** scripts: 18 | - `npm run syncpack:list` – list all used dependencies 19 | - `npm run syncpack:format` – format all package.json files 20 | - `npm run syncpack:mismatch` – list dependencies with different versions (e.g. react@16 and react@17 used in different packages) – can be used in precommit hook to check dependencies sync 21 | - `npm run syncpack:mismatch:fix` – update all dependencies listed by `npm run syncpack:mismatch` to highest used version 22 | 23 | ## Technical details and concepts 24 | 25 | Since this is monorepo all building and starting scripts run from root and share the same webpack configuration. Packages and apps can be referred only by name defined in package.json. 26 | 27 | ### Workspcases 28 | 29 | To work with monorepo you are required to use yarn, since it natively supports workspaces. Workspaces are defined in root package.json: 30 | 31 | ```js 32 | // ... 33 | "workspaces": [ 34 | "src/packages/*", 35 | "src/apps/*" 36 | ], 37 | // ... 38 | ``` 39 | 40 | ### Packages scope 41 | 42 | It is a good idea to use the same scope (`@scope/`) for each package in monorepo to easily identify packages. Although it is not required and packages can use names that include different scopes or no scope at all. 43 | 44 | ### Aliases 45 | 46 | Any package can refer to other package using webpack aliases that are automatically generated. Packages and apps aliases resolve src directory of corresponding package. 47 | 48 | ```js 49 | // example with included @monorepo/hello-world app 50 | import React from 'react'; 51 | import { Text } from '@monorepo/typography'; 52 | import Button from '@monorepo/ui/Button/Button'; 53 | 54 | export default function App() { 55 | return ( 56 |
57 | Welcome to monorepo starter 58 | 59 |
60 | ); 61 | } 62 | ``` 63 | 64 | ### Managing dependencies 65 | 66 | There are two approaches for managing dependencies in monorepo. You can combine both to reach desired behaviour. 67 | 68 | #### Shared dependencies 69 | 70 | - On **core** level: install all globally used dependencies: react, prop-types, classnames, etc. (All dependencies that are expected to use in each package). Install these dependencies as peers on **package** level. 71 | - On **package** level: install package specific dependencies that are used only in this package. 72 | 73 | This approach will allow you to quickly update shared dependencies without need to get through each package and updating versions. 74 | 75 | #### Packages dependencies 76 | 77 | - On **core** level do not install any package related dependencies. 78 | - On **package** level install all package dependencies. 79 | 80 | This approach is pretty heavy and hard to use but will allow you to fully manage versions of dependencies used in each package. 81 | 82 | ### Have questions? 83 | 84 | Let's talk, I'm always open for discussion, please create an issue with your question. 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-monorepo-starter", 3 | "version": "1.0.0", 4 | "author": "Vitaly Rtishchev ", 5 | "dependencies": { 6 | "classnames": "^2.2.6", 7 | "open-color": "^1.7.0", 8 | "prop-types": "^15.7.2", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-hot-loader": "^4.12.21", 12 | "react-router-dom": "^5.2.0", 13 | "uuid": "^8.3.0" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.11.4", 17 | "@babel/plugin-proposal-class-properties": "^7.10.4", 18 | "@babel/plugin-proposal-export-default-from": "^7.10.4", 19 | "@babel/plugin-proposal-export-namespace-from": "^7.10.4", 20 | "@babel/preset-env": "^7.11.0", 21 | "@babel/preset-react": "^7.10.4", 22 | "@storybook/react": "^6.0.16", 23 | "autoprefixer": "^9.8.6", 24 | "babel-eslint": "^10.1.0", 25 | "babel-loader": "^8.1.0", 26 | "chalk": "^4.1.0", 27 | "cross-env": "^7.0.2", 28 | "css-loader": "^4.2.1", 29 | "eslint": "^7.7.0", 30 | "eslint-config-airbnb": "^18.2.0", 31 | "eslint-import-resolver-alias": "^1.1.2", 32 | "eslint-import-resolver-webpack": "^0.12.2", 33 | "eslint-plugin-import": "^2.22.0", 34 | "eslint-plugin-jsx-a11y": "^6.3.1", 35 | "eslint-plugin-react": "^7.20.6", 36 | "eslint-plugin-react-hooks": "^4.1.0", 37 | "fast-glob": "^3.2.4", 38 | "file-loader": "^6.0.0", 39 | "fs-extra": "^9.0.1", 40 | "get-port": "^5.1.1", 41 | "get-port-sync": "^1.0.1", 42 | "html-webpack-plugin": "^4.3.0", 43 | "less-loader": "^6.2.0", 44 | "mini-css-extract-plugin": "^0.10.0", 45 | "open-browser-webpack-plugin": "^0.0.5", 46 | "optimize-css-assets-webpack-plugin": "^5.0.3", 47 | "postcss-loader": "^3.0.0", 48 | "rimraf": "^3.0.2", 49 | "style-loader": "^1.2.1", 50 | "syncpack": "^5.6.10", 51 | "terser-webpack-plugin": "^4.1.0", 52 | "webpack": "^4.44.1", 53 | "webpack-cli": "^3.3.12", 54 | "webpack-dev-server": "^3.11.0", 55 | "yargs": "^15.4.1" 56 | }, 57 | "license": "MIT", 58 | "main": "index.js", 59 | "private": true, 60 | "repository": "https://github.com/rtivital/react-monorepo-starter", 61 | "scripts": { 62 | "build": "node scripts/build-package", 63 | "start": "node scripts/start-app-dev-server", 64 | "storybook": "node storybook/start", 65 | "syncpack:format": "syncpack format", 66 | "syncpack:list": "syncpack list --prod --dev --peer", 67 | "syncpack:mismatch": "syncpack list-mismatches", 68 | "syncpack:mismatch:fix": "syncpack fix-mismatches" 69 | }, 70 | "workspaces": [ 71 | "src/packages/*", 72 | "src/apps/*" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /scripts/build-package.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const chalk = require('chalk'); 4 | const { argv } = require('yargs'); 5 | const getPackagePath = require('./utils/get-package-path'); 6 | const compile = require('./utils/compile'); 7 | 8 | process.env.NODE_ENV = 'production'; 9 | 10 | const packages = argv._; 11 | 12 | const helpMessage = `npm run build script usage: ${chalk.cyan('npm run build @package/name')}\n`; 13 | 14 | function throwError(error) { 15 | process.stdout.write(`${error}\n`); 16 | process.stdout.write(helpMessage); 17 | process.exit(1); 18 | } 19 | 20 | if (packages.length === 0) { 21 | throwError(chalk.red`Packages to build are not defined`); 22 | } 23 | 24 | const paths = []; 25 | 26 | packages.forEach(item => { 27 | const packagePath = getPackagePath(item); 28 | if (!packagePath || !fs.existsSync(path.join(packagePath, 'webpack.config.js'))) { 29 | process.stdout.write(chalk.yellow(`Warning: cannot locate package: ${item}, skipping`)); 30 | } else { 31 | paths.push({ path: packagePath, name: item }); 32 | } 33 | }); 34 | 35 | if (paths.length === 0) { 36 | throwError(chalk.red`Packages to build cannot be located`); 37 | } 38 | 39 | process.stdout.write(`Building packages ${chalk.cyan(paths.map(p => p.name).join(', '))}\n`); 40 | 41 | Promise.all( 42 | paths.map(item => { 43 | /* eslint-disable-next-line global-require, import/no-dynamic-require */ 44 | const config = require(`${item.path}/webpack.config.js`); 45 | 46 | return compile(config) 47 | .then(stats => { 48 | process.stdout.write( 49 | `${chalk.green`✔`} Package ${chalk.cyan(item.name)} was built in ${chalk.green( 50 | `${(stats.toJson().time / 1000).toFixed(2).toString()}s` 51 | )}\n` 52 | ); 53 | }) 54 | .catch((err, stats) => { 55 | if (err) { 56 | process.stdout.write(`${chalk.red`✗`} Package ${chalk.cyan(item.name)} build crashed:\n`); 57 | process.stdout.write(`${chalk.red(err)}\n`); 58 | } 59 | 60 | process.stdout.write(`${chalk.red`✗`} Package ${chalk.cyan(item.name)} build crashed:\n`); 61 | process.stdout.write(`${chalk.red(stats.toString('minimal'))}\n`); 62 | process.exit(1); 63 | }); 64 | }) 65 | ).then(() => { 66 | process.exit(0); 67 | }); 68 | -------------------------------------------------------------------------------- /scripts/start-app-dev-server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { argv } = require('yargs'); 4 | const chalk = require('chalk'); 5 | const WebpackDevServer = require('webpack-dev-server'); 6 | const webpack = require('webpack'); 7 | const getPackagePath = require('./utils/get-package-path'); 8 | 9 | const packageName = argv._[0]; 10 | 11 | if (typeof argv.port === 'number') { 12 | process.env.PORT = argv.port; 13 | } 14 | 15 | const helpMessage = `npm start script usage: ${chalk.cyan('npm start @app/name')}\n`; 16 | 17 | function throwError(error) { 18 | process.stdout.write(`${error}\n`); 19 | process.stdout.write(helpMessage); 20 | process.exit(1); 21 | } 22 | 23 | function throwParsingError(error) { 24 | throwError(`${chalk.red`App`} ${packageName} ${chalk.red`cannot be started: ${error}`}`); 25 | } 26 | 27 | if (!packageName) { 28 | throwError(chalk.red`Error: app name was not specified`); 29 | } 30 | 31 | const packagePath = getPackagePath(packageName); 32 | 33 | if (!packagePath) { 34 | throwParsingError('app with this name does not exist'); 35 | } 36 | 37 | if (!fs.existsSync(path.join(packagePath, 'webpack.config.js'))) { 38 | throwParsingError('it does not have webpack.config.js'); 39 | } 40 | 41 | (async () => { 42 | process.env.NODE_ENV = 'development'; 43 | 44 | /* eslint-disable-next-line import/no-dynamic-require, global-require */ 45 | const config = await require(`${packagePath}/webpack.config.js`); 46 | 47 | if (!config.devServer) { 48 | throwParsingError('app does not have dev server configuration in webpack config'); 49 | } 50 | 51 | new WebpackDevServer(webpack(config), { historyApiFallback: true }).listen(config.devServer.port, 'localhost', error => { 52 | if (error) { 53 | /* eslint-disable-next-line no-console */ 54 | console.error(error); 55 | } 56 | }); 57 | })(); 58 | -------------------------------------------------------------------------------- /scripts/utils/compile.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | module.exports = function compile(config) { 4 | const compiler = webpack(config); 5 | 6 | return new Promise((resolve, reject) => { 7 | compiler.run((err, stats) => { 8 | if (err) { 9 | reject(err, new Error('Invalid webpack configuration')); 10 | } 11 | 12 | if (stats.hasErrors()) { 13 | reject(stats); 14 | } 15 | 16 | resolve(stats); 17 | }); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /scripts/utils/get-package-alias.js: -------------------------------------------------------------------------------- 1 | const getSrcMap = require('./get-src-map'); 2 | 3 | module.exports = function getPackageAlias(packageName) { 4 | const packagesData = getSrcMap(); 5 | delete packagesData[packageName]; 6 | return packagesData; 7 | }; 8 | -------------------------------------------------------------------------------- /scripts/utils/get-package-path.js: -------------------------------------------------------------------------------- 1 | const getPackagesData = require('./get-packages-data'); 2 | 3 | module.exports = function getPackagePath(name) { 4 | return getPackagesData()[name]; 5 | }; 6 | -------------------------------------------------------------------------------- /scripts/utils/get-packages-data.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const glob = require('fast-glob'); 4 | 5 | const REPO_ROOT = path.join(__dirname, '../../'); 6 | const CORE_PACKAGE_JSON = fs.readJsonSync(path.join(REPO_ROOT, './package.json')); 7 | 8 | module.exports = function getPackagesData() { 9 | const packages = glob.sync( 10 | CORE_PACKAGE_JSON.workspaces.map(workspace => 11 | path.join(__dirname, `../../${workspace}/package.json`) 12 | ) 13 | ); 14 | 15 | return packages.reduce((acc, item) => { 16 | const packageRoot = item.replace('/package.json', ''); 17 | const { name } = fs.readJsonSync(path.join(packageRoot, 'package.json')); 18 | acc[name] = packageRoot; 19 | return acc; 20 | }, {}); 21 | }; 22 | -------------------------------------------------------------------------------- /scripts/utils/get-src-map.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getPackagesData = require('./get-packages-data'); 3 | 4 | module.exports = function getSrcMap() { 5 | const packagesData = getPackagesData(); 6 | 7 | return Object.keys(packagesData).reduce((acc, name) => { 8 | acc[name] = path.join(packagesData[name], './src'); 9 | return acc; 10 | }, {}); 11 | }; 12 | -------------------------------------------------------------------------------- /src/apps/hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo/hello-world", 3 | "description": "Test application", 4 | "version": "1.0.0", 5 | "author": "Vitaly Rtishchev ", 6 | "dependencies": {}, 7 | "devDependencies": {}, 8 | "license": "MIT", 9 | "main": "dist/lib.js", 10 | "peerDependencies": { 11 | "classnames": "^2.2.6", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1" 15 | }, 16 | "scripts": {} 17 | } 18 | -------------------------------------------------------------------------------- /src/apps/hello-world/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text } from '@monorepo/typography'; 3 | import Button from '@monorepo/ui/Button/Button'; 4 | 5 | export default function App() { 6 | return ( 7 |
8 | Welcome to monorepo starter 9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/apps/hello-world/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App'; 4 | 5 | render(, document.getElementById('app')); 6 | -------------------------------------------------------------------------------- /src/apps/hello-world/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAppConfig = require('../../../webpack/get-app-config'); 3 | 4 | module.exports = getAppConfig({ 5 | base: path.join(__dirname, './'), 6 | mode: process.env.NODE_ENV || 'production', 7 | }); 8 | -------------------------------------------------------------------------------- /src/packages/typography/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo/typography", 3 | "description": "Typography components", 4 | "version": "1.0.0", 5 | "author": "Vitaly Rtishchev ", 6 | "dependencies": {}, 7 | "devDependencies": {}, 8 | "license": "MIT", 9 | "main": "dist/lib.js", 10 | "peerDependencies": { 11 | "classnames": "^2.2.6", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1" 14 | }, 15 | "scripts": {} 16 | } 17 | -------------------------------------------------------------------------------- /src/packages/typography/src/Text/Text.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classes from './Text.styles.less'; 4 | 5 | export default function Text({ children, ...others }) { 6 | return ( 7 |
8 | {children} 9 |
10 | ); 11 | } 12 | 13 | Text.propTypes = { 14 | children: PropTypes.node, 15 | }; 16 | -------------------------------------------------------------------------------- /src/packages/typography/src/Text/Text.story.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Text from './Text'; 3 | 4 | export default { 5 | title: 'Text', 6 | component: Text, 7 | }; 8 | 9 | const Template = args => ; 10 | 11 | export const GeneralUsage = Template.bind({}); 12 | 13 | GeneralUsage.args = { 14 | children: 'General usage', 15 | }; 16 | -------------------------------------------------------------------------------- /src/packages/typography/src/Text/Text.styles.less: -------------------------------------------------------------------------------- 1 | .text { 2 | color: inherit; 3 | font-family: Helvetica, sans-serif; 4 | -webkit-font-smoothing: antialiased; 5 | } 6 | -------------------------------------------------------------------------------- /src/packages/typography/src/index.js: -------------------------------------------------------------------------------- 1 | export Text from './Text/Text'; 2 | -------------------------------------------------------------------------------- /src/packages/typography/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getPackageConfig = require('../../../webpack/get-package-config'); 3 | 4 | module.exports = getPackageConfig({ base: path.join(__dirname, './') }); 5 | -------------------------------------------------------------------------------- /src/packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@monorepo/ui", 3 | "description": "UI react components to build internal tools", 4 | "version": "1.0.0", 5 | "author": "Vitaly Rtishchev ", 6 | "dependencies": {}, 7 | "devDependencies": {}, 8 | "license": "MIT", 9 | "main": "dist/lib.js", 10 | "peerDependencies": { 11 | "classnames": "^2.2.6", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1" 14 | }, 15 | "scripts": {} 16 | } 17 | -------------------------------------------------------------------------------- /src/packages/ui/src/Button/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Text } from '@monorepo/typography'; 4 | import classes from './Button.styles.less'; 5 | 6 | export default function Button({ children, ...others }) { 7 | return ( 8 | 11 | ); 12 | } 13 | 14 | Button.propTypes = { 15 | children: PropTypes.node, 16 | }; 17 | -------------------------------------------------------------------------------- /src/packages/ui/src/Button/Button.story.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from './Button'; 3 | 4 | export default { 5 | title: 'Button', 6 | component: Button, 7 | }; 8 | 9 | const Template = args =>