Skip to content
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

Convert global to window for browser targets. #1747

Closed
gdborton opened this issue Jul 17, 2018 · 32 comments
Closed

Convert global to window for browser targets. #1747

gdborton opened this issue Jul 17, 2018 · 32 comments

Comments

@gdborton
Copy link

gdborton commented Jul 17, 2018

🐛 bug report

I'm working on prototyping our build in parcel to measure performance. One of the things that I noticed different between webpack and parcel through my work is that webpack will wrap calls to global in a function that exposes the global variable as window. Parcel bundles just fail when run in the browser with global is undefined.

🤔 Expected Behavior

In a compiled bundle targeting the browser, console.log(global); should behave the same as console.log(window);

😯 Current Behavior

console.log(global); throws with global is undefined

💁 Possible Solution

One solution is to follow webpack's solution. For any file that includes a reference to global wrap the contents of the file with:

(
  function (global) {
    // body of original module.
  }.call(this, require('parcels-global-shim')
)

🔦 Context

💻 Examples

@gdborton
Copy link
Author

Closing this for now as I couldn't repro in a smaller repository. It may be something specific to our build. I'll reopen if I find more on this.

@gdborton gdborton reopened this Jul 17, 2018
@gdborton
Copy link
Author

gdborton commented Jul 18, 2018

Re-opened as I was able to track this down to using type="module" in my script tags. We don't actually need this, so we can work around, but figured I'd leave this open for tracking purposes.

@rinick
Copy link

rinick commented Aug 26, 2018

I can reproduce this in my react project when I add type="module"

related source code
https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/__forks__/setImmediate.js

parcel generated js

"..\\node_modules\\fbjs\\lib\\setImmediate.js":[function(require,module,exports) {
var global = arguments[3];
/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 */

'use strict';

// setimmediate adds setImmediate to the global. We want to make sure we export
// the actual function.

require('setimmediate');
module.exports = global.setImmediate;
},{"setimmediate":"..\\node_modules\\setimmediate\\setImmediate.js"}],

@LasseRosenow
Copy link

LasseRosenow commented Mar 24, 2019

#2833

I have the same problem with global by trying to build a website based on web components in which case type="module" is necessary.

@mischnic
Copy link
Member

Ideally, something babel based could polyfill globalThis. But strangely enough @babel/preset-env doesn't do this.

@ljharb
Copy link

ljharb commented Aug 26, 2020

Hi! If Parcel is trying to be a bundler for node modules, why is it not correctly providing all browser-compatible node globals, in this case, global?

@mischnic
Copy link
Member

With Parcel 2, console.log(global) is turned into

var $parcel$global =
	typeof globalThis !== "undefined"
		? globalThis
		: typeof self !== "undefined"
		? self
		: typeof window !== "undefined"
		? window
		: typeof global !== "undefined"
		? global
		: {};
console.log($parcel$global);

for both Node and Browser targets (though it's not really necessary for Node...)

@ljharb
Copy link

ljharb commented Aug 26, 2020

@mischnic it's necessary for packages that support node/browser versions that predate globalThis. Sounds like this issue may be fixed in Parcel 2, then?

@JakeChampion
Copy link

It's not fixed in parcel 2 as that is what I was using when I came across this issue

@mischnic
Copy link
Member

Could someone please give a code sample that doesn't work as expected?

@JakeChampion
Copy link

I'll make it now 👍

@JakeChampion
Copy link

I came across the issue by using @formatjs/intl-numberformat/polyfill package which depends upon the has-symbols which is what was throwing the error due to global not being defined.

I can no longer recreate this issue in parcel 2 😅 Here is my code sample which does now work -- https://codesandbox.io/s/quirky-microservice-0gnm9

I'm pretty sure I hit this issue only 3 days ago but I never committed that work to my repo because it wasn't working, instead I raised an issue on [has-symbols[(https://github.com/inspect-js/has-symbols/pull/18).

I guess I could have been using parcel 1 by mistake 🤔 .

@mischnic
Copy link
Member

mischnic commented Aug 26, 2020

Is it possible that parcel build works but parcel build --no-scope-hoist (which is more similar to dev serve mode) doesn't?

@JakeChampion
Copy link

Is it possible that parcel build works but parcel build --no-scope-hoist (which is more similar to dev serve mode) doesn't?

I guess it is possible but I only used parcel serve when I hit the issue

@mischnic
Copy link
Member

I couldn't come up with a situation where it isn't replaced correctly with Parcel 2. If you do come across a case where it isn't, please open a new issue with a code sample.

@Porges
Copy link

Porges commented Sep 6, 2020

@mischnic I’m seeing this not work with has-symbols when coming in as a dependency of semantic-ui-react. Here’s a sample. It works okay in CodeSandbox but if you download it and npm install; npm start it should fail with:

Uncaught TypeError: Cannot read property 'Symbol' of undefined

The line in question looks like:

var origSymbol = global.Symbol;

There’s a related issue here (inspect-js/has-symbols#4) but AFAICT parcel should be rewriting this?

@Porges
Copy link

Porges commented Sep 7, 2020

On the project I’m working on (and probably the previous example) build does work and build --no-scope-hoist does not work.

A workaround for using serve is to remove the type=module attribute, and revert that before doing build.

@mischnic mischnic reopened this Sep 7, 2020
@Akash187
Copy link

Akash187 commented Jul 4, 2021

@mischnic it's necessary for packages that support node/browser versions that predate globalThis. Sounds like this issue may be fixed in Parcel 2, then?

Could someone please give a code sample that doesn't work as expected?

Hi @mischnic I am still facing the issue while trying to add Google Maps to my project. I am using the latest version of the parcel(v2.0.0-beta.3.1). I have created a repo to demonstrate the problem https://github.com/Akash187/typescript-project.

Basically, the Google Maps script runs a function name initMap as a callback which needs to be in global scope but unfortunately, it is not there and I am getting an error "initMap is not a function". But once I explicitly add the function initMap on the window object it works fine. I want it to work fine without explicitly adding a function to the window.

@bminer
Copy link

bminer commented Jul 26, 2021

The issue here is subtle. As far as I can tell, this is an issue with dev-prelude.js:

modules[name][0].call(
module.exports,
localRequire,
module,
module.exports,
this,
);

Note that this is being used on line 76 as the 4th argument to the module. Within modules using global, I have found that they are prefixed with the line let global = arguments[3];. In other words, this (on line 76) becomes global within each module.

Generally, this defaults to window within the browser's context; however, in strict mode, this will be undefined. <script> tags with type="module" are in strict mode! I believe this is why users only experience this problem with <script type="module" src="..."> tags.

One possible solution is to use globalObject instead of this on line 76. globalObject is defined here:

/* eslint-disable no-undef */
var globalObject =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {};

Thoughts?

@devongovett
Copy link
Member

In the nightly releases this shouldn't be the case anymore. type="module" gets removed now when not actually targeting ES modules.

@bminer
Copy link

bminer commented Jul 26, 2021

@devongovett What about when targeting ES modules?

@devongovett
Copy link
Member

When building for production, the dev-prelude is not used. Instead, global is replaced by $parcel$global which maps to:

$parcel$global: `var $parcel$global =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {};

@QiYuTechDev
Copy link

parcel is boken(in browser env) with package: inspect-js/available-typed-arrays#11

`global` is not defined

@bminer
Copy link

bminer commented Aug 19, 2021

As far as I can tell, this is now working in [email protected]

Can others confirm?

@jeffpeck10x
Copy link
Contributor

This is not working for me in [email protected]

When I parcel serve, it builds perfectly for the browser. When I parcel build, it produces code that has global in it and fails.

@mischnic
Copy link
Member

When I parcel build, it produces code that has global in it and fails.

Can you share a code sample for that? Building a file just containing console.log(global); does perform this replacement for me: #1747 (comment)

@jeffpeck10x
Copy link
Contributor

jeffpeck10x commented Aug 20, 2021

I don't think I can make a reasonable sample. I will note that my project is a monorepo that imports other packages from inside the project. One of those packages imports from deck.gl, which uses luma. The error that I was getting was from lumg.gl and when I inspected the line, it was looking for global.luma.

Also, I found that if I build with --no-scope-hoist it builds something that does not throw that error, except that the css imports appear to be broken.

Again, this is something that works perfectly with parcel serve. In fact, if I do parcel serve and then manually kill that process, I can then take the contents of dist/ and serve those, and that works. (Although, I'm not sure how I could easily automate that.)


I wish that there was a way to make parcel build produce the exact same output in dist as parcel serve. While the outputted javascript is huge, it would help to establish a baseline of what works and what doesn't. @mischnic If you have a way to do that, it would definitely help, not only for my particular use-case, but for debugging issues like this in general.

@jeffpeck10x
Copy link
Contributor

jeffpeck10x commented Aug 20, 2021

If it helps, this is my package.json:

{
  "name": "..."
  "version": "0.0.0",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "start": "parcel serve src/index.html --open $BROWSER --no-cache",
    "build": "parcel build src/index.html --no-cache --no-scope-hoist"
  },
  "browserslist": "> 0.2%",
  "dependencies": {
...
  },
  "devDependencies": {
...
  }
}

Note, the --no-scope-hoist.

If I remove private: true, parcel complains:

@parcel/resolver-default: External dependency "@babel/runtime" is not declared in package.json.

I'm not sure if that is related?

@jeffpeck10x
Copy link
Contributor

Trying once more with --no-optimize and scope hoisting ON, I get:

init.js:31 Uncaught TypeError: Cannot read property 'luma' of undefined

The line referred to from the luma source on init.js:31 is:

if (global.luma && global.luma.VERSION !== VERSION) {
  throw new Error(`luma.gl - multiple VERSIONs detected: ${global.luma.VERSION} vs ${VERSION}`);
}

That line appears to correspond to:

if ($a8abb625118bbfa0$exports.global.luma && $a8abb625118bbfa0$exports.global.luma.VERSION !== $daf23e938e432229$var$VERSION) throw new Error("luma.gl - multiple VERSIONs detected: ".concat($a8abb625118bbfa0$exports.global.luma.VERSION, " vs ").concat($daf23e938e432229$var$VERSION));

in the javascript file created in the dist directory.

where:

$a8abb625118bbfa0$exports = (parcelRequire("dz9qQ"));

I see that var $parcel$global = ... in there.

I am wondering if the problem is that parcelRequire is not attaching .global to the requires?

@jeffpeck10x
Copy link
Contributor

Aha! yes, the parcel serve output produces this:

if (_env.global.luma && _env.global.luma.VERSION !== VERSION) throw new Error("luma.gl - multiple VERSIONs detected: ".concat(_env.global.luma.VERSION, " vs ").concat(VERSION));

as opposed to the line that I pasted above with $a8abb625118bbfa0$exports.global.luma.

I apologize for the multiple replies. I think this might have something to do with it though.

@jeffpeck10x
Copy link
Contributor

jeffpeck10x commented Aug 27, 2021

@mischnic I tried to recreate the issue by making a small generic repo that has a similar structure to my actual project:
https://github.com/jeffpeck10x/parcel-test

I was not able to recreate the exact same error, but I am running into a similar issue where the demo runs perfectly, yet the parcel build produces code that will not run.

git clone https://github.com/jeffpeck10x/parcel-test.git
cd parcel-test
yarn install

yarn dev
# go to http://localhost:5000/, everything works, yay!

yarn build
yarn serve-static-demo
# go to http://localhost:5000/, View console: "Uncaught ReferenceError: Log is not defined"

If you'd like, I can make a new issue and describe this there, although I think it falls under the same general issue. Here, it fails when it tries to do:

class $85d5d0e08a5e1191$export$9099ad97b570f7c {
  ...
}

Log.VERSION = $ekQcv.VERSION;

It should be doing:

$85d5d0e08a5e1191$export$9099ad97b570f7c.VERSION = ...

@mischnic
Copy link
Member

@jeffpeck10x See #6790

Regarding #1747 (comment): I've just tested Parcel 2 with and without scope-hoisting: global is replaced in both cases.

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

No branches or pull requests