Example monorepo
A monorepo where apps share components might look like this:
monorepo
|--apps
|--app1
|--app2
|--components
|--component1
|--component2
|--component3
Setup Info
- this monorepo uses lerna w/ yarn workspaces
- this repo currently uses an alpha release of react-scripts
How create a new app
Go to ./apps
directory and run:
create-react-app my-app --template typescript
probably you are get an error like this:
There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.
The react-scripts package provided by Create React App requires a dependency:
"babel-jest": "^24.9.0"
Don't try to install it manually: your package manager does it automatically.
However, a different version of babel-jest was detected higher up in the tree:
/home/oliveira/Projects/stone/cra-monorepo-examples/node_modules/babel-jest (version: 23.6.0)
Manually installing incompatible versions is known to cause hard-to-debug issues.
If you would prefer to ignore this check, add SKIP_PREFLIGHT_CHECK=true to an .env file in your project.
That will permanently disable this message but you might encounter other issues.
For now, let's do the react recomendation to pass this warning, nothing will happen.
Execute this command:
echo SKIP_PREFLIGHT_CHECK=true > .env
In this point our application already is working!! But it's far from working with monorepo. To make it work with monorepo lets do some more configs.
In my-app/package.json
put the code below
"sourceWorkspaces": [
"components/*"
]
and add a devDependencies react-scripts by @bradfordlemley. This dependency make the magic happen
"devDependencies": {
"@bradfordlemley/react-scripts": "^2.0.402",
}
In my-app/tsconfig.json
put the code below
"include": [
"src",
"../../components/**/*"
]
In the end your my-app/package.json
must be like this:
below, @project-ui/button
is a shared component o/
{
"name": "ts-project1",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "3.3.0",
"@project-ui/button": "0.1.0"
},
"devDependencies": {
"@bradfordlemley/react-scripts": "^2.0.402",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^24.0.0",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"typescript": "~3.7.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"sourceWorkspaces": ["components/*"]
}
and your my-app/tsconfig.json
must be like this:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve"
},
"include": ["src", "../../components/**/*"]
}
Using your first shared component inside your react-app (CRA)
You can import a shared component by the name defined in your shared-components/package.json
respected. You can copy it name and put in the my-app/package.json
of your react app as dependencies
{
"dependencies": {
"@project-ui/button": "0.1.0"
}
}
Then, import this dependency in your react-app component and use them
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import Button from "@project-ui/button";
const App: React.FC = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
<Button>Ola mundo</Button>
</header>
</div>
);
};
export default App;
How create a shared component
Go to ./components/
and and create new folder representing your new shared component
components
| --new-shared-component
| --src
| --index.tsx
| --index.story.tsx
| --styles.ts
| --package.json
you can create a package.json
using
$ npm init -y
and define a main
to you entripoint, for example:
"main": "src/index.tsx"
index.tsx file example
import React from "react";
import { Button as SButton } from "./styles";
export default function Button({ children }: { children: string }) {
return <SButton>{children}</SButton>;
}
storybook file example
import React from "react";
import { storiesOf } from "@storybook/react";
import Button from ".";
storiesOf("Button", module).add("default", () => <Button>Ola mundo</Button>);
styles.ts file example with styled component
import styled from "styled-components";
export const Button = styled.button`
padding: 5px;
border-radius: 4px;
background-color: white;
border: 1px solid #eee;
`;
unit test file example
import React from "react";
import { render } from "@testing-library/react";
import Button from ".";
test("renders 'Ola mundo'", () => {
const { getByText } = render(<Button>Ola mundo</Button>);
const linkElement = getByText(/Ola mundo/i);
expect(linkElement).toBeInTheDocument();
});