ember-auto-import icon indicating copy to clipboard operation
ember-auto-import copied to clipboard

Breaks when using script defer

Open mydea opened this issue 4 years ago • 11 comments

I have an app that is not using ember-auto-import in its application code (as far as I can tell).

I noticed that I get a runtime error:

l.js:2 Uncaught ReferenceError: require is not defined
    at eval (l.js:2)
    at Object.../../../../../tmp/broccoli-20413LssqYisKTYOQ/cache-305-webpack_bundler_ember_auto_import_webpack/l.js (chunk.app.9a9a5f13351ef9d0df2b.js:29)
    at __webpack_require__ (chunk.app.9a9a5f13351ef9d0df2b.js:53)

Where l.js is:

window._eai_r = require;
window._eai_d = define;

When I remove ember-auto-import from my devDependencies, the error goes away.

I would just remove it, but according to https://github.com/emberjs/ember-qunit/blob/master/docs/migration.md it is needed for ember-qunit for the tests (although the tests do work without it, as far as I can tell :thinking: )

mydea avatar Jul 07 '21 07:07 mydea

What version of ember-auto-import?

What were you changing when this problem appeared?

ef4 avatar Jul 07 '21 15:07 ef4

Not 100% sure, as the app did not fail (and tests passed), the error is only visible in the console (which I only just noticed).

I would guess it happend when upgrading from ember-auto-import 1.x to 2.x - I just tried downgrading it and the error went away!

I will try to see if I can get a reproduction somehow...!

mydea avatar Jul 08 '21 14:07 mydea

OK, after a lot of trial and error, I found the culprit!

In this app, defer was added to the vendor JS file in index.html:

<script src="{{rootURL}}assets/vendor.js" defer></script>

As it uses fastboot/prember and thus does not need the JS to work.

It works if:

  • I remove defer
  • or I downgrade to ember-auto-import v1
  • If I visit /tests it also does not trigger the error

Here is a reproduction repo: https://github.com/mydea/ember-issue-reproductions/commit/4948624aa766b1d12edc067b545e0d096a630d13

mydea avatar Jul 09 '21 07:07 mydea

We need to find a clearer place to document this, but this is a known limitation, see https://github.com/ef4/ember-auto-import#customizing-build-behavior:

publicAssetURL: the public URL to your /assets directory on the web. Many apps won't need to set this because we try to detect it automatically, but you will need to set this explicitly if you're deploying your assets to a different origin than your app (for example, on a CDN) or if you are using

ef4 avatar Jul 09 '21 13:07 ef4

I've encountered the same issue when upgrading to v2. Application uses defer for scripts, and the config already contains publicAssetURL: '/assets', I've also tried publicAssetURL: 'http://localhost:4200/assets', but with no luck.

Should the combination of defer and publicAssetURL: '/assets' work out of the box?

bobisjan avatar Jul 18 '21 09:07 bobisjan

This is a duplicate of #418.

rwjblue avatar Jul 28 '21 20:07 rwjblue

Even when using the new insertScriptsAt I'm unable to use defer in scripts after 2.x I tried with and without publicAssetURL. Not sure if I'm not understanding the new syntax, but this seems like a regression from 1.x.

jrjohnson avatar Aug 18 '21 19:08 jrjohnson

Isn't the issue here that defer causes the script to be loaded and evaluated asynchronously, and the eai generated chunk probably runs before vendor.js, which would explain why require and define are undefined?

Deferred scripts are guaranteed to run in order...

Scripts with the defer attribute will execute in the order in which they appear in the document. MDN

But I haven't found a definite answer to the question what happens when a normal script follows a deferred one. I think it wouldn't make much sense if the normal script would wait for the deferred one, as then there is no deferring happening at all.

But that would mean that the eai generated chunk that contains window._eai_r = require; runs after vendor.js in this scenario:

<script src="/assets/vendor.js" defer></script>
<script src="/assets/app.js" defer></script>
<script src="/assets/chunk.12345.js"></script>

Maybe we must make eai also add defer to the scrips tags in generated in this case?

simonihmig avatar Dec 17 '21 19:12 simonihmig

We've managed to get this working as documented now with app/index.html containing.

<script defer src="{{rootURL}}assets/vendor.js"></script>
<auto-import-scripts defer entrypoint="app"> </auto-import-scripts>
<script defer src="{{rootURL}}assets/app.js"></script>

and our ember-cli-build.js with:

autoImport: {
  insertScriptsAt: 'auto-import-scripts',
},

jrjohnson avatar Dec 17 '21 20:12 jrjohnson

@jrjohnson Thanks for sharing, cool that this works now!

I think it's a bit unfortunate that this is kind of "insider info". I expect this to have quite a positive effect, have you measured this change in term of e.g. Lighthouse score?

Maybe this should be the default when installing FastBoot, e.g. by modifying index.html with ember-cli-fastboot's default blueprint?

@ef4 what about Embroider? This PR seems related, but doesn't cover all what eai has here. Should we aim for feature parity, by introducing the same in Embroider?

simonihmig avatar Dec 19 '21 19:12 simonihmig

Yes, we should land the feature in https://github.com/embroider-build/embroider/pull/588 for embroider as well.

ef4 avatar Dec 20 '21 16:12 ef4