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

Bug: React not defined (with TypeScript) #1199

Closed
felixrabe opened this Issue Apr 16, 2018 · 18 comments

Comments

Projects
None yet
9 participants
@felixrabe
Copy link

felixrabe commented Apr 16, 2018

I'm aware that this is very likely a duplicate of some other bug. I'll research and close ASAP, just dumping my findings here for reference.


Edit – Related issues:

  • Typescript + Preact doesn't work right now #1095

Edit – Using Node v9.11.1 btw. (Forgot to mention it below.)


Creating a new project like this (I've copy and pasted these lines to verify): (macOS 10.13.4, yarn 1.6.0, Parcel 1.7.1)

mkdir 054-parcel-typescript-react
cd 054-parcel-typescript-react

yarn add --dev parcel-bundler

echo '<div id="app"></div> <script src="./app.tsx"></script>' > index.html

echo $'import React from "react"\nimport {render} from "react-dom"\nrender(<h1>Hi there</h1>, document.getElementById("app"))' > app.tsx

... running Parcel:

./node_modules/.bin/parcel index.html

😯 Current Behavior

... leads to an empty page on http://localhost:1234/ and this console message: (Firefox 59)

ReferenceError: React is not defined

🤔 Expected Behavior

I'd expect the page to contain a big fat Hi there and no console error.

Resulting files from above process

(plus a .gitignore can be found here: https://github.com/felixrabe/e-2018-054-parcel-typescript-react)

package.json

{
  "devDependencies": {
    "parcel-bundler": "^1.7.1",
    "typescript": "^2.8.1"
  },
  "dependencies": {
    "react": "^16.0.0",
    "react-dom": "^16.3.2"
  }
}

index.html

<div id="app"></div> <script src="./app.tsx"></script>

app.tsx

import React from "react"
import {render} from "react-dom"
render(<h1>Hi there</h1>, document.getElementById("app"))
@felixrabe

This comment has been minimized.

Copy link

felixrabe commented Apr 16, 2018

Just TypeScript (leaving out React) works

mkdir 055-parcel-just-typescript-works
cd 055-parcel-just-typescript-works
yarn add --dev parcel-bundler
echo '<body><script src="app.tsx"></script></body>' > index.html
echo 'const s: string = "hi there" ; document.body.appendChild(document.createElement("div")).textContent = s' > app.tsx
./node_modules/.bin/parcel index.html

This gives the expected result at http://localhost:1234/: "hi there" and no console error.

(Repo: https://github.com/felixrabe/e-2018-055-parcel-just-typescript-works)

@felixrabe

This comment has been minimized.

Copy link

felixrabe commented Apr 16, 2018

(This is not a duplicate of the above comment, but the opposite variant – trying out just React instead of just TypeScript.)

Just React (leaving out TypeScript) works

mkdir 056-parcel-just-react-works
cd 056-parcel-just-react-works
yarn add --dev parcel-bundler
echo '<div id="app"></div> <script src="app.jsx"></script>' > index.html
echo $'import React from "react"\nimport {render} from "react-dom"\nrender(<h1>Hi there</h1>, document.getElementById("app"))' > app.jsx
./node_modules/.bin/parcel index.html

This gives the expected result at http://localhost:1234/: big "Hi there" and no console error.

(Repo: https://github.com/felixrabe/e-2018-056-parcel-just-react-works)

@felixrabe

This comment has been minimized.

Copy link

felixrabe commented Apr 16, 2018

I've just read in the 1.7 blog post something about a rewritten resolver. So I tried again using Parcel 1.6.2 to compare former behavior:

First example: TypeScript AND React in Parcel 1.6.2

mkdir 054-parcel-typescript-react
cd 054-parcel-typescript-react
yarn add --dev parcel-bundler@1.6.2  # <= NEW: added '@1.6.2'
echo '<div id="app"></div> <script src="./app.tsx"></script>' > index.html
echo $'import React from "react"\nimport {render} from "react-dom"\nrender(<h1>Hi there</h1>, document.getElementById("app"))' > app.tsx
./node_modules/.bin/parcel index.html

This already fails in the Terminal (which to me is an improvement compared to 1.7's silent non-Terminal console-only failure):

Felixs-iMac:054-parcel-typescript-react fr$ ./node_modules/.bin/parcel index.html
Server running at http://localhost:1234 
⏳  Building app.tsx...
yarn add v1.6.0
warning package.json: No license field
warning No license field
[1/4] Resolving packages...
yarn add v1.6.0
warning package.json: No license field
warning No license field
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ typescript@2.8.1
info All dependencies
└─ typescript@2.8.1
warning No license field
Done in 1.83s.
🚨  /Users/fr/j/2018/experiments/p-1.6/054-parcel-typescript-react/app.tsx:4:26: Cannot resolve dependency 'react-dom'
  2 | import {render} from "react-dom"
  3 | render(<h1>Hi there</h1>, document.getElementById("app"))
> 4 | 
    | ^
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
🚨  /Users/fr/j/2018/experiments/p-1.6/054-parcel-typescript-react/app.tsx:3:22: Cannot resolve dependency 'react'
  1 | import React from "react"
  2 | import {render} from "react-dom"
> 3 | render(<h1>Hi there</h1>, document.getElementById("app"))
    |                       ^
  4 | 

(I did not bother checking the page in the browser after that message. Just ^C-ed the server.)

Second example: TypeScript ONLY in Parcel 1.6.2

mkdir 055-parcel-just-typescript-works
cd 055-parcel-just-typescript-works
yarn add --dev parcel-bundler@1.6.2  # <= NEW: added '@1.6.2'
echo '<body><script src="app.tsx"></script></body>' > index.html
echo 'const s: string = "hi there" ; document.body.appendChild(document.createElement("div")).textContent = s' > app.tsx
./node_modules/.bin/parcel index.html

Works correctly as with Parcel 1.7.1.

Third example: React ONLY in Parcel 1.6.2

mkdir 056-parcel-just-react-works
cd 056-parcel-just-react-works
yarn add --dev parcel-bundler@1.6.2  # <= NEW: added '@1.6.2'
echo '<div id="app"></div> <script src="app.jsx"></script>' > index.html
echo $'import React from "react"\nimport {render} from "react-dom"\nrender(<h1>Hi there</h1>, document.getElementById("app"))' > app.jsx
./node_modules/.bin/parcel index.html

This time, Parcel 1.6.2 behaves worse than Parcel 1.7.1 and gives me this Terminal error message, similar to the first example above:

Felixs-iMac:056-parcel-just-react-works fr$ ./node_modules/.bin/parcel index.html
Server running at http://localhost:1234 
🚨  /Users/fr/j/2018/experiments/p-1.6/056-parcel-just-react-works/app.jsx:1:18: Cannot resolve dependency 'react'
> 1 | import React from "react"
    |                   ^
  2 | import {render} from "react-dom"
  3 | render(<h1>Hi there</h1>, document.getElementById("app"))
  4 | 

The browser page at http://localhost:1234/ shows:

🚨 Build error, check the console for details.

But the Browser console is empty (probably referring to the system console here).

To fix it, I can ^C the server, manually install the missing deps, and restart the server:

yarn add react react-dev
./node_modules/.bin/parcel index.html

... and http://localhost:1234/ correctly greets me with a big fat "Hi there" and no error.

@gnijuohz

This comment has been minimized.

Copy link
Contributor

gnijuohz commented Apr 17, 2018

I encountered this as well recently. Using import * as React from 'react' worked for me. Maybe you can use it as a workaround for now.

@shunia

This comment has been minimized.

Copy link

shunia commented Apr 17, 2018

@felixrabe Use what @gnijuohz suggests and it will be fine.
BTW, this is not a parcel issue, the way react output it's definition file and to export a general(cjs and umd and es compatible export) namespace caused this.

@marvinhagemeister

This comment has been minimized.

Copy link

marvinhagemeister commented Apr 17, 2018

@felixrabe This is a known difference between how babel and typescript handle es5 code when imported via es6 imports respectively. If you want the babel behaviour in TypeScript you can enable it via the --allowSyntheticDefaultImports option.

@gnijuohz

This comment has been minimized.

Copy link
Contributor

gnijuohz commented Apr 17, 2018

Thanks @shunia and @marvinhagemeister, I'm new to Typescript :)
I tried using the following tsconfig.json,

{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true
    }
}

and that didn't fix it. Went back to the docs for this option and it says,

Allow default imports from modules with no default export. This does not affect code emit, just typechecking.

Since it doesn't affect code emit, then it's expected nothing changes in our case...

@dtinth

This comment has been minimized.

Copy link

dtinth commented Apr 18, 2018

This tsconfig.json solved it for me:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "jsx": "react"
  }
}

(Make sure to make a small change to the .jsx file to invalidate the compilation cache.)

esModuleInterop makes TypeScript module resolution from ES→CommonJs behave like webpack’s and Babel’s behavior (create a synthetic namespace with only default export). This allows import React from 'react' to work fine. In my opinion, Parcel should turn this option on by default.

@gnijuohz

This comment has been minimized.

Copy link
Contributor

gnijuohz commented Apr 18, 2018

@dtinth esModuleInterop: true is in fact the default in Parcel. If you just use tsx: react in the config, it works as well.

So currently two approaches work:

  • no tsconfig.json, use import * as React from 'React'
  • tsconfig.json, with the following,
{
  "compilerOptions": {
    "jsx": "react"
  }
}
@gnijuohz

This comment has been minimized.

Copy link
Contributor

gnijuohz commented Apr 18, 2018

Hi @DeMoorJasper currently Parcel's default for jsx is preserve, what are the downsides of changing it to 'react'? (then react native users will have to override this option?)

@DeMoorJasper

This comment has been minimized.

Copy link
Member

DeMoorJasper commented Apr 18, 2018

@gnijuohz the original thought behind it was the whole pipeline concept inside parcel. Where it starts with typescript and than goes through ts-compiler than babel, therefore I thought this was a good default as babel would take care of JSX

@DeMoorJasper

This comment has been minimized.

Copy link
Member

DeMoorJasper commented Apr 20, 2018

I'll close this off as there are several responses that answer the question

@felixrabe

This comment has been minimized.

Copy link

felixrabe commented Apr 25, 2018

Yes, this is fine to be closed. I'm still investigating, but the key to resolve my issue seems to be (exactly as @gnijuohz stated above) to add tsconfig.json with

{
  "compilerOptions": {
    "jsx": "react"
  }
}

This seems to be all that is required, and it makes sense to require it. The option esModuleInterop is already activated by default in Parcel itself, which also makes sense to me as it is "highly recommended" by the TypeScript project itself.

@fitfab

This comment has been minimized.

Copy link

fitfab commented May 1, 2018

clearing the cache rm .cache/* and adding this to the tsconfig.json worked for me.

{
  "compilerOptions": {
    "jsx": "react"
  }
}
@dtinth

This comment has been minimized.

Copy link

dtinth commented May 1, 2018

IMO, "jsx": "react" should also be the default in parcel-bundler to maintain the spirit of zero configuration. I would expect import React from 'react' to just work regardless of whether I use TypeScript or JavaScript.

@DeMoorJasper

This comment has been minimized.

Copy link
Member

DeMoorJasper commented May 1, 2018

@dtinth Feel free to open a PR, the original thought behind the configuration of ts-compiler was that react should be processed by babel, as we use something we call the processing pipeline, which basically processes, the asset as follows
.ts => ts-compiler => .js => babel => js packager => bundle

But after thinking about it, it might not make so much sense after all, because using the original concept you would have to config babel to process react (although in theory parcel should detect react code automatic and change babel config accordingly)

Feel free to open a PR with the improved config

@ryansmith94

This comment has been minimized.

Copy link

ryansmith94 commented Aug 13, 2018

@felixrabe thanks for this bit in particular...

The option esModuleInterop is already activated by default in Parcel itself, which also makes sense to me as it is "highly recommended" by the TypeScript project itself.

Just caught us out when importing "react-focus-trap" in our component library because we were using Parcel to preview the component in development and the TypeScript compiler without that option for production.

@Offirmo

This comment has been minimized.

Copy link

Offirmo commented Jan 4, 2019

This should be in the doc. Will try to add it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment