parcel-bundler / parcel

The zero configuration build tool for the web. đŸ“Ļ🚀
https://parceljs.org
MIT License
43.47k stars 2.27k forks source link

Parcel 2: Using the same package in different packages result in multiple instance of said package #3971

Open Banou26 opened 4 years ago

Banou26 commented 4 years ago

🐛 bug report

Using the same package(e.g React) in multiple packages from a monorepo that are being aliased result in using different instances of React, even though they have the same versions

I also tried specifying them as peerDependency but same result

🌍 Your Environment

Software Version(s)
Parcel 2.0.0-alpha.3.2
mischnic commented 4 years ago

When do you observe "result in using different instances of React", (in) which monorepo package are you building?

Banou26 commented 4 years ago

https://github.com/Banou26/parcel-bug-3971

e.g, if you just have React as a peerDependency in the Foo package which is imported from Main, it'll throw

ℹī¸ @parcel/package-manager: yarn: Saved lockfile.
ℹī¸ @parcel/package-manager: yarn: Saved 26 new dependencies.
ℹī¸ @parcel/package-manager: yarn: Direct dependencies
ℹī¸ @parcel/package-manager: yarn: All dependencies
🚨 Error: Cannot find module 'react' from '/home/banou/dev/parcel-alias-dependency/packages/foo'
Error: Cannot find module 'react' from '/home/banou/dev/parcel-alias-dependency/packages/foo'
    at ResolverRunner.resolve (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/core/lib/ResolverRunner.js:124:15)
    at async DepPathRequestRunner.runRequest (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/core/lib/RequestTracker.js:356:43)
    at async Promise.all (index 2)
    at async AssetGraphBuilder.build (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/core/lib/AssetGraphBuilder.js:175:7)
    at async Parcel.build (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/core/lib/Parcel.js:370:11)
    at async Parcel.startNextBuild (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/core/lib/Parcel.js:275:21)
    at async PromiseQueue._runFn (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/utils/lib/PromiseQueue.js:98:7)
    at async PromiseQueue._next (/home/banou/dev/parcel-alias-dependency/packages/main/node_modules/@parcel/utils/lib/PromiseQueue.js:85:5)

And if you have React as dependency in both, the result in window is false for

foo.js

import React from 'react'

export default React

main.js

import React from 'react'
import FooReact from 'foo'

console.log(FooReact === React)

Which makes the use of features like React Hooks impossible across multiple packages since they need the same instance of React to work

Banou26 commented 4 years ago

Also if I try to ls their version, everything seems fine from npm regarding my packages

[banou@banou-pc main]$ npm ls react
@parcel-alias-dependency/main@0.0.1 /home/banou/dev/parcel-alias-dependency/packages/main
├─â”Ŧ @parcel-alias-dependency/foo@0.0.1 -> /home/banou/dev/parcel-alias-dependency/packages/foo
│ └── react@16.12.0 
├─â”Ŧ parcel@2.0.0-alpha.3.2
│ ├─â”Ŧ @parcel/config-default@2.0.0-alpha.3.1
│ │ └─â”Ŧ @parcel/reporter-cli@2.0.0-alpha.3.1
│ │   └── react@16.12.0  deduped
│ └── react@16.12.0  deduped
└── react@16.12.0
mischnic commented 4 years ago

(peerDependencies aren't handled properly yet, though I'm not completely sure if that's the problem here: https://github.com/parcel-bundler/parcel/pull/3753)

Banou26 commented 4 years ago

BTW, I get the same result from lerna's bootstrapping replacing the aliases

Banou26 commented 4 years ago

Oh, wow, if I install parcel at the root of the monorepo and build the package via parcel ./packages/main/index.html, the react instance become shared https://github.com/Banou26/parcel-bug-3971/commit/ecec1fcf2ce62cccc78df578130911b2d975d7e0

Is that the intended behavior ? Doesn't seem right to me

Edit: Actually, after some more advanced tests, I get mixed results from building at the root, it's so weird, gonna post more soon

Banou26 commented 4 years ago

https://github.com/Banou26/parcel-bug-3971

If we yarn install at the root and build from the root, the instance of React is shared between the packages.

If we yarn install at the root and either of the sub packages and build from the root, the instance isn't shared anymore.

If we build from a sub package, the instance isn't shared either.

Thing is, you have to yarn install in the sub packages, else parcel throws an error saying that it doesn't find the package.

This error weirdly doesn't happen for React, maybe because react is implicitly imported from parcel?

But yeah, this makes monorepos kind of unusable when working with a framework like react.

Banou26 commented 4 years ago

I've been trying hard to make this work.

I couldn't find a way to make parcel work with this so I tried with webpack, to my big surprise, webpack had the same exact problem.

Luckily I found a nice little thread that had the same problem using webpack, the solution was this: https://github.com/facebook/create-react-app/issues/6027#issuecomment-454515804

webpack.config.js

{
  resolve: {
    modules: [path.resolve('node_modules'), 'node_modules']
  }
}

Apparently there's some big problems related to this: https://github.com/facebook/create-react-app/pull/6207

Any idea how i could make this work with parcel ?

mischnic commented 4 years ago

Thing is, you have to yarn install in the sub packages, else parcel throws an error saying that it doesn't find the package.

You should be using yarn's workspace feature: https://yarnpkg.com/lang/en/docs/workspaces/

If you used that and https://github.com/parcel-bundler/parcel/pull/3753 was merged, I think it should work by specifying react as a peer dependency in foo.

A workaround: react is bundled only once if you specify don't specify it as a devDependency but only as a peerDependency in foo and ignore the yarn warning.

zlls commented 4 years ago

I'm not sure if this works with v2 but with v1 you can add an alias property to your package.json to fix this issue:

"alias": {
  "react": "./node_modules/react"
}
matthewjosephtaylor commented 2 years ago

I'm not sure if this works with v2 but with v1 you can add an alias property to your package.json to fix this issue:

"alias": {
  "react": "./node_modules/react"
}

This work-around fixes the issue with v2. And this is still a bug in v2 where parcel doesn't seem to correctly handle peerDependencies and includes multiple reacts.