Parcel: Tree shaking with react-router-dom

Created on 23 Mar 2019  路  10Comments  路  Source: parcel-bundler/parcel

馃悰 bug report

When I run parcel build src/index.html --out-dir build/prod --experimental-scope-hoisting I got the following error

馃毃  ../node_modules/react-router-dom/esm/react-router-dom.js does not export 'BrowserRouter'
    at replaceExportNode (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/parcel-bundler/src/scope-hoisting/concat.js:54:13)
    at ReferencedIdentifier (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/parcel-bundler/src/scope-hoisting/concat.js:317:20)
    at newFn (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/visitors.js:230:17)
    at NodePath._call (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/path/context.js:53:20)
    at NodePath.call (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/path/context.js:40:17)
    at NodePath.visit (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/path/context.js:88:12)
    at TraversalContext.visitQueue (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/context.js:118:16)
    at TraversalContext.visitMultiple (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/context.js:85:17)
    at TraversalContext.visit (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/context.js:144:19)
    at Function.traverse.node (/Users/zhaoyao/workspace/ai-pja-web-2/node_modules/@babel/traverse/lib/index.js:94:17)

The relative file is simple:

import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import _ from "lodash";

import routes from "./routes";

function AppRouter() {
  return (
    <Router>
      <Switch>
        {_.map(routes, (component, path) => (
          <Route exact key={path} path={path} component={component} />
        ))}
      </Switch>
    </Router>
  );
}

export default AppRouter;

馃帥 Configuration (.babelrc, package.json, cli command)

// .babelrc
{
  "presets": ["@babel/preset-react"]
}
// package.json
{
  "name": "ai-pja-web",
  "version": "0.1.0",
  "scripts": {
    "dev": "parcel src/index.html --out-dir build/dev",
    "build": "del build/prod & parcel build src/index.html --out-dir build/prod",
    "build:tree-shaking": "del build/prod & parcel build src/index.html --out-dir build/prod --experimental-scope-hoisting",
    "start": "serve -s build/prod",
    "flow": "flow"
  },
  "husky": {
    "hooks": {
      "pre-commit": "pretty-quick --staged"
    }
  },
  "dependencies": {
    "lodash": "^4.17.11",
    "react": "^16.8.5",
    "react-dom": "^16.8.5",
    "react-router-dom": "^5.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.4.0",
    "@babel/preset-react": "^7.0.0",
    "del-cli": "^1.1.0",
    "flow-bin": "^0.95.1",
    "husky": "^1.3.1",
    "parcel-bundler": "^1.12.3",
    "parcel-plugin-bundle-visualiser": "^1.2.0",
    "parcel-plugin-static-files-copy": "^2.0.0",
    "prettier": "^1.16.4",
    "pretty-quick": "^1.10.0",
    "serve": "^10.1.2"
  }
}

馃 Expected Behavior

The app compiled

馃槸 Current Behavior

Error

Bug Confirmed Bug 馃尦 Tree Shaking

All 10 comments

@zhaoyao91 I can't reproduce this. Your code compiles fine for me (are you using Parcel 1.12.3 ?).
I also tested an example from the react-router-dom docs:

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";

function Index() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
      </div>
    </Router>
  );
}

ReactDOM.render(<AppRouter/>, document.getElementById("app"));

@mischnic hi, sorry I should have included a demo repo before. Here it is:

https://github.com/zhaoyao91/parcel-issue-demo-2837

I have tried to re-install deps but it npm run build:tree-shaking still report error.

my node is v10.13.0 installed by nvm, and my OS is mac 10.14.3

Strange, this only happens when the entry point imports the actual code.

// index.js
import "./run.js";

// run.js
import * as React from "react";
import { render } from "react-dom";

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={() => <div>Test</div>} />
      </Switch>
    </Router>
  );
}

render(<App />, document.getElementById("root"));

https://github.com/mischnic/parcel-issue-demo-2837

Somehow, import "./run.js" doesn't "scan" the dependencies from that module (therefore not adding react-router-dom to the bundle). import x from "run.js" and exporting anything from run.js works.

@mischnic thanks for your hint. As a workaround, I included an empty function as a flag to prevent side-effect being tree-shaken, that works.

Look forward to it being solved 馃憤

Duplicate of #2039?

Despite the same title, #2039 happens because of the tsconfig used: https://github.com/parcel-bundler/parcel/issues/2039#issuecomment-466778511
Here, no typescript is involved

@mischnic thanks for your hint. As a workaround, I included an empty function as a flag to prevent side-effect being tree-shaken, that works.

Could you elaborate on your workaround? I'm running into the same issue with react-router-dom but I don't quite follow where or how an empty function could be used to prevent it :-/

@dkadrios
if you currently do this:

// index.js
import "app.js";

// run.js
import * as React from "react";
import { render } from "react-dom";

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={() => <div>Test</div>} />
      </Switch>
    </Router>
  );
}

render(<App />, document.getElementById("root"));

then changing that to this might work work:

// index.js
import something from "app.js";
something();

// run.js
import * as React from "react";
import { render } from "react-dom";

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={() => <div>Test</div>} />
      </Switch>
    </Router>
  );
}

render(<App />, document.getElementById("root"));
export default function(){};

Clever hack.

Unfortunately, this doesn't work in my case (or react-router-dom has changed in a way that prevents it)

I'll keep digging as this could become a deal-breaker now that our bundles are growing larger

Maybe you can reduce your code and and post a reproduction?
(It seems like the original issue is no longer present in the Parcel 2 branch)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

algebraic-brain picture algebraic-brain  路  3Comments

dotdash picture dotdash  路  3Comments

dsky1990 picture dsky1990  路  3Comments

Niggler picture Niggler  路  3Comments

davidnagli picture davidnagli  路  3Comments