parcel: ES Module type=module cause "parcelRequire is not defined" error

šŸ› bug report

Using ES Module in main HTML cause ā€œparcelRequire is not definedā€ error.
If we use type=ā€œmoduleā€, minimal code in Getting Started (Parcel official) make this error.

šŸŽ› Configuration (.babelrc, package.json, cli command)

zero configuration.

šŸ¤” Expected Behavior

If we write super simple code with ES Module like below,

<!-- index.html -->
<html>
<body>
<script src="./index.js" type="module"></script>
</body>
</html>
// index.js
console.log("hello parcel");

I hope it is bundled properly and get ā€œhello parcelā€ in console without error.

😯 Current Behavior

In console,

hello parcel.   index.js 1
Uncaught ReferenceError: parcelRequire is not defined at test.904c6489.js:10

šŸ’ Possible Solution

Apparently ES Module cause this error.
When I removed type=ā€œmoduleā€ from script element, no error observed.

It suggest that we can avoid error by avoiding type=ā€œmoduleā€ for parcel.
But I think this approach is not good.
ES Module (type=ā€œmoduleā€) is generally available (now 2018-05, all popular browsers supported!!), so ES module is straight forward way to import module.

šŸ”¦ Context

I do not know what problem is caused by this error.

šŸŒ Your Environment

Software Version(s)
Parcel parcel-bundler@1.8.1
Node (problem happen both) 8.9.3 & 9.11.1
npm/Yarn
Operating System Windows10 Pro, build 17134

Thank you good library, I hope this project become better and better.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 68
  • Comments: 53 (8 by maintainers)

Commits related to this issue

Most upvoted comments

since you use type=ā€œmoduleā€,why use parcel?

Mainly for module resolution.
And for old browser users (ES module not supported, need Bundle.)

Please see my PR above. It’s pretty disappointing that there was no fix or PR for this show-stopper bug that requires literally a 4-character fix, for a 30k star project used by so many people šŸ˜•

This is an issue with the way parcelRequire is defined in Strict Mode. ES6 modules are by default executed in strict mode (AFIK), which prohibits implicit variable declerations.

Here’s what Parcel’s output looks like:

parcelRequire = (function (modules, cache, entry, globalName) {
  // Save the require from previous bundle to this closure if any
  var previousRequire = typeof parcelRequire === 'function' && parcelRequire;
  var nodeRequire = typeof require === 'function' && require;

  function newRequire(name, jumped) {

Notice how the global parcelRequire is implicitly declared without let/var!

By editing the output file and naively adding let before parcelRequire I was able to eliminate the issue. However, adding let can affect scoping…

An alternative that works is changing it to window.parcelRequire, but that would break in Node; which probably doesn’t really matter since we can avoid doing that in --target node.

I ran into this in the wild in a slightly separate case when attempting to require a library I bundled with Parcel in a Webpack project. The Webpack project had a top level "use strict" pragma automatically injected which is what broke everything in the same way as this issue.

A good point to use Parcel in this case is the minification and files with hashes

I have the same problem in different context. I’m trying to consume parcel-bundled module from my webpack-bundled app. Since ES6 modules (my app) run in strict context an attempt to consume the module results in assignment to undefined variable and crashes my app.

@devongovett I think stripping type="module" from the output script tag is a good solution.

My usage of parcel is not quite as intended. I prefer to write completely valid js/css that runs in modern browsers, and dev using a simple static server and modern browser (with no build step). I want to use parcel only for producing production builds (both to compile to ES5 for older browsers, and to concatenate small scripts).

Since parcel doesn’t concatenate multiple script tags from a single html page, you have to import them from another js file. But the only way to get that to work in development without a build step is to use type="module" (alternatively, if parcel would bundle consecutive <script> tags in an html file, that would also work - but it seems like a bigger change).

I know my use case isn’t as intended, but I think it has a lot of merit, and parcel very nearly supports it.

This is not a bug. Don’t use <script type="module"> with parcel. Parcel expects its generated scripts not to run in strict mode so it can assign to the global object and such. I don’t see a reason to use <script type="module"> with Parcel since Parcel already compiles modules to ES5.

In order to work around this error when users mistakenly specify <script type="module"> in their HTML, we could strip the type attribute in the HTML packager when we write the output bundle.

Maybe it makes sense for Parcel to remove type="module" for an entry that is being built?

@nestarz This will be possible with Parcel 2 (and it’s already implemented).

temporary fix for your work environment or localhost - https://twitter.com/dfkaye/status/1044693110700171264

Add a <script>var parcelRequire;</script> to the <head> solve this issue for me. I have to use type="module" with Parcel because I’m using TypeScript.

yeah I’m having this issue when I use a raw .ts file as my input and I bundle my node_modules with it into a single file e.g.

parcel build ./src/index.ts --target node --bundle-node-modules

when i run it in node I’ll get an error like

ReferenceError: parcelRequire is not defined

when I look at the non-minified version I can see

// eslint-disable-next-line no-global-assign
parcelRequire = (function (modules, cache, entry, globalName) {

so my guess is the assumption was that it would globally assign. it appears that it doesn’t.

Obviously if I add var in front of it the lexical scoping lookup of JavaScript assigns it to global. but from what I can gather that would break things for other environments i.e. not --target node

I came up with a workaround

parcel build ./src/index.ts --target node --bundle-node-modules --out-dir ./dist --no-minify --out-file main.js && sed -i '' '1s;^;var ;' ./dist/main.js

obviously the bummer here is I can’t use watch because how would I append it for my needs.

anyway is there an ETA for this fix? the set-up (or lack thereof) is super convenient so I’d like to use Parcel for a recommended way of doing something except this is kind of a blocker.

nicer work around: yarn add parcel-plugin-wrapper --dev

.assetWarpper.js

const path = require('path')

const CWD = process.cwd()
const PACKAGE = require(path.join(CWD, 'package.json'))

const varRequire = async ({name, bundler}) => {
    // name = app.ere76r5e76r5e76r.js
    if (name.split('.').pop() === 'js' && bundler.options.production) {
        return {
            header: `var parcelRequire = undefined;`,
            footer: ``
        }
    }
}

module.exports = varRequire;

With alpha 2 (which should be released in the next couple of days), you can do

<script type="module" src="index.js">

and index.js will automatically be emitted as an ES module.

Or if you want to build a library, specify it via package.json#targets.name.outputFormat: package.json

{
	"main": "dist/index.js",
	"targets": {
		"main": {
			"outputFormat": "esmodule"
		}
	}
}

(and running something like parcel build src/index.js)

Adding the Firefox error message so this issue pops up in searches:

ReferenceError: assignment to undeclared variable parcelRequire

Can this issue be resolved without altering the generated output (e.g. assigning to window.parcelRequire instead of parcelRequire) at the moment?

if you strip <script type="module"> you need to add defer to get the same behaviour

@devongovett ava appears to load tests as esmodules. see https://github.com/parcel-bundler/parcel/issues/2213 - i can certainly prepend my compiled test files with var parcelRequire = undefined;, that seems to work, but I think there is a use case here. I’d certainly rather this than having to juggle multiple build systems

My use is to use native ES modules for dev and then bundler for production for older browsers. I also happen to be using snowpack.

If you say Parcel will not work in this case without rewriting the script include structure between dev and deploy it cannot be used. 😦

While I get the error everyhting seems to work. So does the error matter?

I get this error whether I use type="module" or not. And the weird thing is that it only appears when I deploy it to vercel. But it works with npm start locally. And it only happens if index.js includes an import statement.

EDIT: more testing It simply does not work with npm run build. But npm start works.

Observation: In addition to that LOL fix from Twitter, this issue can also be hackishly fixed by inserting any regular script tag ( not type=module ) before your type=module entry point. Without it Parcel still generates these errors.

<script src="seemingly_anything.js"></script>
<script src="es6_entry_point.js" type="module"></script>

Sooo, am I reading this correctly? Parcel does not allow for a standard ES Module output?

it should not be hard to add var or let or const