Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Lerna and/or Yarn Workspaces #1333

Open
rovansteen opened this issue Jan 1, 2017 · 202 comments
Open

Support Lerna and/or Yarn Workspaces #1333

rovansteen opened this issue Jan 1, 2017 · 202 comments

Comments

@rovansteen
Copy link
Contributor

rovansteen commented Jan 1, 2017

Are there any plans to support Lerna? By default Lerna uses the packages directory to store the packages. Right now the content of that folder is not transpiled so it's not possible to use ES6 code in there. It would be nice to have the possibility to use Lerna to create a monorepo application with create-react-app.

I've tried using the src directory with Lerna, but that conflicts with not ignoring node_modules directory inside the src directory, so it gives a ton of lint errors when you do so.

@rgbkrk
Copy link

rgbkrk commented Feb 11, 2017

I'm using a create-react-app created app within a lerna monorepo and am quite pleased with it. Steps:

~/someRepo/packages$ create-react-app app
~/someRepo/packages$ cd ..
~/someRepo$ lerna bootstrap
~/someRepo$ cd packages/app
~/someRepo/packages/app$ npm run start

🎉

You can require your other packages by name within the create-react-app after adding them to app's package.json and running lerna bootstrap.

@gaearon
Copy link
Contributor

gaearon commented Feb 11, 2017

This is pretty nice. Does Babel transpilation still work in this case though?

@rgbkrk
Copy link

rgbkrk commented Feb 11, 2017

The individual packages under packages/ need to do a transpilation step. They'll be linked via lerna but the main entrypoints still need to be ES5.

@rgbkrk
Copy link

rgbkrk commented Feb 11, 2017

I would prefer to not have to transpile all our packages when used in an overall app and yet I know that when we publish to npm we need them to be ES5-consumable.

@jedrichards
Copy link

For me the holy grail of Lerna support with CRA would look something like,

/web-clients
├── package.json
└── packages
    ├── api
    │   └── package.json
    ├── components
    │   └── package.json
    ├── cra-app-1
    │   └── package.json
    ├── cra-app-2
    │   └── package.json
    └── something-else
        └── package.json

The idea here is that we have a monorepo for web clients that can contain multiple CRA-based apps as peers that have access to a few shared other packages (e.g. a lib of shared presentational components, maybe a lib with some api calling utils, anything else). The CRA apps should be able to require code from the other packages and Babel should know to transpile such code coming from within the monorepo automatically.

Is this possible with Lerna and CRA at the moment? Any related issues or info I can look at?

@rgbkrk
Copy link

rgbkrk commented Sep 7, 2017

While I'm no longer using create-react-app (using next.js), this is how we do it in nteract for various components and the notebook-preview-demo (the next.js app): https://github.com/nteract/nteract/tree/master/packages

@tbillington
Copy link

Yarn added workspaces in 1.0 recently, don't know how it impacts this but thought it might be worth mentioning.

@ashtonsix
Copy link
Contributor

ashtonsix commented Nov 29, 2017

@gaearon I've got babel transpilation working alongside Lerna in a private fork of CRA.

Add something like this to the end of /config/paths:

module.exports.lernaRoot = path
  .resolve(module.exports.appPath, '../')
  .endsWith('packages')
  ? path.resolve(module.exports.appPath, '../../')
  : module.exports.appPath

module.exports.appLernaModules = []
module.exports.allLernaModules = fs.readdirSync(
  path.join(module.exports.lernaRoot, 'packages')
)

fs.readdirSync(module.exports.appNodeModules).forEach(folderName => {
  if (folderName === 'react-scripts') return
  const fullName = path.join(module.exports.appNodeModules, folderName)
  if (fs.lstatSync(fullName).isSymbolicLink()) {
    module.exports.appLernaModules.push(fs.realpathSync(fullName))
  }
})

Webpack configs:

// before
{
  include: paths.appSrc
}
// after
{
  include: paths.appLernaModules.concat(paths.appSrc)
}

Jest config:

// before
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$'],

// after
transformIgnorePatterns: [
  // prettier-ignore
  '[/\\\\]node_modules[/\\\\](?!' + paths.allLernaModules.join('|') + ').*\\.(js|jsx|mjs)$'
]

Don't have the time to make a PR & add tests, comments, etc. but should be easy for someone else to do

@shendepu
Copy link

Great!! @ashtonsix . I also have the lerna set up with CRA based on your code.

My folder structure is

lerna
  -- packages
      -- local package 1 (not published to npm registry)
      -- local package 2
      -- local package 3 
      -- local package ... 
      -- real app (created with CRA)
  -- lerna.json

For anyone else, the key for webpack in CRA app to transpile jsx is add lerna packages absolute path in include of babel-loader

BTW: I have it done without forking CRA, but used a mechanism to extend CRA webpack config, you may find more detail steps in #1328 (comment)

@bradfordlemley
Copy link
Contributor

I've created an example monorepo and PR 3741 that adds support for it.

monorepo
  |--packages.json: workspaces: ["apps/*", "comps/*", "cra-comps/*"] <-- (yarn workspace)
  |--lerna.json: packages: ["apps/*", "comps/*", "cra-comps/*"] <-- (lerna w/o yarn workspace)
  |--apps
    |--cra-app1 <-- basic cra-app, doesn't use any monorepo comps
    |--cra-app2 <-- basic cra-app, doesn't use any monorepo comps
    |--cra-app3 <-- uses some monorepo comps
      |--package.json: dependencies: ["comp1": <vsn>, "comp2": <vsn>]
  |--comps
    |--comp3 <-- not a cra component?
      |--package.json: main: build/index.js
      |--build/index.js  <-- don't transpile?
      |--index.js
      |--index.test.js <-- don't test?
  |--cra-comps
    |--comp1 <-- standard-ish cra component with JSX
      |--package.json: main: comp1.js
      |--comp1.js
      |--comp1.test.js
    |--comp2 <-- cra component with dependency on other cra-comp in monorepo
      |--package.json: dependencies: ["comp1"]
      |--comp2.js: import comp1 from 'comp1'
      |--comp2.test.js

I assume that it is agreed that cra-comps should be transpiled just like app src (including JSX) since that seems to be the request from above comments, but some further questions about desired functionality:

  1. Which (if any) tests from cra-comps should run when running app tests? (only comps that the app specifies as dependencies?)
  2. Should files from cra-comps be linted when building app?
  3. Should a directory structure be enforced for cra-comps? (e.g. files need to be under /src)
  4. How to flag components that should not be treated as cra-comps? (or should cra-comps be by convention, e.g. well-known directory in monorepo?)

Is this the correct place to try to get the answers for those questions?

@iamchathu
Copy link

I'm also getting error

../shared/lib/icons/coffee.d.ts 2:0
Module parse failed: The keyword 'interface' is reserved (2:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import React, { CSSProperties } from 'react';
> interface Props {
|     width: CSSProperties['width'];
|     height: CSSProperties['height'];

This is shared typescript package(shared) which pointed to build folder(lib) with definitions files and the warning comes for definition files. Seems skipLibCheck is ignored due to symlink nature of Lerna and yarn Workspace.

I have same kind of setup for another up and it doesn't gives errors but for this it gives errors.

@cwellsx
Copy link

cwellsx commented Jun 8, 2020

My first attempt to integrate CRA with lerna was based on https://juliangaramendy.dev/monorepo-demo/

That was pretty successful and orthodox: components are built separately (before the CRA app), and then are then available as dependencies of the CRA app like any other dependency in node_modules would be. Unfortunately I found a problem with it, which was that when when I debugged the CRA app, the debugger couldn't set breakpoints in nor properly step into the TypeScript in component packages. That might (I'm not sure) be because WebPack is transpiling the node_modules which makes their source maps out of date.

So I looked for another way to do this and found it here:

The steps are:

The are various ways to implement this change to webpack.config.js (many of which are suggested in comments earlier in this thread):

Anyway I did it using react-app-rewired as described in
https://github.com/minheq/monorepo-cra-source-map

I needed to do something else (one more thing) as well: because the TypeScript source in the component packages are in src subdirectories, I had to change the import statements in the app to import from client\src instead of from client. Ideally I should have been able to, instead, fix this with baseUrl and paths in the tsconfig.json -- however these are not supported by CRA.

Given this new method (i.e. editing webpack.config.js) it's sufficient to run yarn build in the ui-react project -- its build will now also build all its dependencies. It's no longer necessary to build or pre-build the dependencies explicitly.

My example of making this change is at cwellsx/view@8d046d9

@JeremyGrieshop
Copy link

My first attempt to integrate CRA with lerna was based on https://juliangaramendy.dev/monorepo-demo/

That was pretty successful and orthodox: components are built separately (before the CRA app), and then are then available as dependencies of the CRA app like any other dependency in node_modules would be. Unfortunately I found a problem with it, which was that when when I debugged the CRA app, the debugger couldn't set breakpoints in nor properly step into the TypeScript in component packages. That might (I'm not sure) be because WebPack is transpiling the node_modules which makes their source maps out of date.

There's another issue here #8071 discussing it along with a pull request, if you would like to check it out and upvote it.

@geoyws
Copy link

geoyws commented Jun 10, 2020

@alexnitta here you are https://github.com/anthanh/lerna-react-ts-transpile-modules

You can run the app and edit the import @monorepo/common/module1-2 or the implementation packages/common/module1/index.ts and the changes will be updated.

This worked well for me. Saved my tail.

@calvinwyoung
Copy link

@cwellsx I'm doing the same thing to import shared packages into my CRA package, and this works beautifully. But one thing I'm struggling with is getting absolute import paths working within my shared package.

I can use absolute imports in my CRA app by setting "baseUrl": "./" in tsconfig.json. This allows me to write import statements like this within the CRA package: import { Foo } from 'src/components/Foo'.

I'd also like to do this within my shared package, but updating baseUrl in tsconfig.json within the shared package obviously doesn't work since babel doesn't read that file.

Is there another way to get absolute import paths working within shared packages?

@ricklove
Copy link

ricklove commented Jul 10, 2020

Successful setup:

  • All source code acts like it is part of the main project:
  • hot loading works great with changes anywhere
  • vscode acts like all the source code is all part of the same codebase: navigation, refactoring, etc. works as intended, (even auto imports works most of the time)

Using same setup as:

https://github.com/anthanh/lerna-react-ts-transpile-modules/tree/master/packages/webapp

  • modify package.json
    • change scripts to use react-app-rewired
    • add react-app-rewired and customize-cra devDependencies
  • add config-overrides.js
    • add packages paths to babel config

Also:

  • Yarn workspaces v2
  • tsconfig paths (note, we are using a module prefix @lib/ to make it easy to distinguish package usage)
{
    "compilerOptions": {
        "paths": {
            "@lib/utils/*": [
                "packages/utils/*"
            ],
            "@lib/utils-react/*": [
                "packages/utils-react/*"
            ],
        }
    }
}
  • typescript packages: Don't use src folder in your packages, just put the source files at the root. This allows the import without the 'src' everywhere. It's not that bad really, it just means there is an extra package.json file sitting in the middle of the source files.
  • Bonus: We are actually also using a git submodule with nested yarn workspaces since most teams work in their own repos with out current setup - but this allows us to share code easily, while still having our own independent repos

@farah
Copy link

farah commented Jul 11, 2020

@ricklove thanks. do you mind putting all that in a repo? I think it would help a lot of people.

@raphaelpc
Copy link

@ricklove thanks, i was just able to make my lerna monorepo work following those same steps!
Also, thanks to your description i also found this great tutorial from 2019 that teachs, in more detail, how to make all that:
https://medium.com/@Notif.me/extract-your-react-components-as-dependencies-in-a-lerna-cra-project-without-ejecting-cf76236ffe82

Anyway, it would be great if create-react-app could make it easier to add those customizations without requiring those external packages.

😄

@jamiehaywood
Copy link

would be fantastic to have this feature. I followed the same tutorial that @raphaelpc followed and it does the job, but it doesn't feel right to change the start script to "start": "react-app-rewired start". Would be really keen to work on this, but it would be my first contribution to a big open source project 🙈.

@Jekins
Copy link

Jekins commented Mar 16, 2021

This is my solution:

Add this to the end of /config/paths:

module.exports.appLernaModules = []
const folderLernaPackageName = '@my-monorepo' // this is the name of your packages folder in package.json. For example: name: '@my-monorepo/utils'
const lernaRoot = path.resolve(module.exports.appPath, '../../')
const lernaPackages = path.join(lernaRoot, 'packages') // this is the name of your packages folder in monorepo

const regexpFolderLernaPackage = new RegExp(`^${folderLernaPackageName}\\/`, 'g')
const lernaPackagesFromDependencies = Object.keys(
  require(resolveApp('package.json')).dependencies
)
  .filter((packageName) => packageName.startsWith(folderLernaPackageName))
  .map((packageName) => packageName.replace(regexpFolderLernaPackage, ''))

fs.readdirSync(lernaPackages).forEach((folderName) => {
  const isDependencyNeed = lernaPackagesFromDependencies.includes(folderName)

  if (isDependencyNeed) {
    module.exports.appLernaModules.push(path.join(lernaPackages, folderName))
  }
})

Replace this in /config/webpack.config.js:

// before
{
  include: paths.appSrc
}

// after
{
  include: paths.appLernaModules.concat(paths.appSrc)
}

Thanks for the idea @ashtonsix

@baerrach
Copy link

After

yarn eject

I stopped reading.

@ricklove
Copy link

That feeling you get when you have the same problem a year later and find an answer written by your past self. Thanks past @ricklove ! Note: your memory is not great.

@Bluefitdev
Copy link

Hey guys.. any update on this i'm still stuck today trying to setup my existing cra app with lerna. Anyone tried to do it with craco? I'm getting weird error:

TypeError: this.updater.enqueueCallback is not a function

@ricklove
Copy link

ricklove commented Apr 6, 2022

Hey @ricklove! I'm back form the future. Note: jest testing is still an issue...

@cmdcolin
Copy link

cmdcolin commented Apr 6, 2022

I asked a (currently unanswered) stackoverflow question about this same issue here and just got reminded of this thread since I'm subscribed :)

https://stackoverflow.com/questions/71729055/creating-a-basic-monorepo-setup-that-can-get-incremental-updates-to-packages

would be really interested in this. I'd like the individual packages to all get built incrementally and then refer to the dist folders instead of the src at dev time, but it seems like many people refer to the actual src directories of their monorepo packages which is a bit awkward

@captDaylight
Copy link
Contributor

@ricklove I'm here for all your time traveling

@marcofugaro
Copy link
Contributor

Guys, use turborepo, this issue isn't gonna be adressed

@0xdevalias
Copy link

Guys, use turborepo

Or https://nx.dev/

@alamothe
Copy link

This is possible with Yarn PNP and CRA but not out of the box, you need to set up some kind of auto-build as soon as any files change based on watchman. It's not that difficult to get it working though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
2.0
  
Needs Decision
Development

No branches or pull requests