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 – 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
... leads to an empty page on http://localhost:1234/ and this console message: (Firefox 59)
ReferenceError: React is not defined
I'd expect the page to contain a big fat Hi there and no console error.
(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"))
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)
(This is not a duplicate of the above comment, but the opposite variant – trying out just React instead of just TypeScript.)
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)
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:
mkdir 054-parcel-typescript-react
cd 054-parcel-typescript-react
yarn add --dev [email protected] # <= 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
└─ [email protected]
info All dependencies
└─ [email protected]
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.)
mkdir 055-parcel-just-typescript-works
cd 055-parcel-just-typescript-works
yarn add --dev [email protected] # <= 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.
mkdir 056-parcel-just-react-works
cd 056-parcel-just-react-works
yarn add --dev [email protected] # <= 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.
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.
@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.
@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.
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...
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.
@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:
import * as React from 'React'
{
"compilerOptions": {
"jsx": "react"
}
}
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?)
@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
I'll close this off as there are several responses that answer the question
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.
clearing the cache rm .cache/*
and adding this to the tsconfig.json
worked for me.
{
"compilerOptions": {
"jsx": "react"
}
}
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.
@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
@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.
This should be in the doc. Will try to add it.
I had to do:
import * as React from 'react'
import _, { PureComponent } from 'react'
not super clean haha
@tj did you try the solutions above?
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react"
}
}
We ran into this even though the TS config we extend already included "jsx": "react"
, so I guess Parcel overrides it. Setting it again worked:
{
// This config already sets "jsx" to "react"
"extends": "shared/tsconfig",
// But we still have to override Parcel's default config
"compilerOptions": {
"jsx": "react"
}
}
@denkristoffer Thx for saving my life! And it seems a bug to me.
One more important thing. You'll need to rm -rf .cache
after changing tsconfig.json
I have the same issue and I don't use typescript at all. This already produces the error:
index.jsx
import { useState } from "react"
I could fix it by simply adding import React from "react"
to the top of each imported React Component.
When I install both parcel and typescript on my computer, how can I make parcel use the global typescript instead of downloading a copy when I run the parcel command
You'll need to rm -rf .cache after changing tsconfig.json
That's the main catch here. Why doesn't this happen automatically?
"jsx": "react"
You saved me!!
Most helpful comment
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.