Ky: Using umd.js with traditional <script> tag causes ReferenceError: can't access lexical declaration ky before initialization

Created on 21 Oct 2019  路  4Comments  路  Source: sindresorhus/ky

Copying the example given in the FAQ of the Readme and running in Firefox 69.0.3:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd.js"></script>
<script>
(async () => {
    const ky = ky.default;

    const parsed = await ky('https://jsonplaceholder.typicode.com/todos/1').json();

    console.log(parsed.title);
    //=> 'delectus aut autem
})();
</script>

Results in:
ReferenceError: can't access lexical declaration 'ky' before initialization

All 4 comments

Just tested in FF and in Chrome, can confirm this issue exists. I don't think the UMD build actually exposes ky as a global.

Edit: I wonder if we change this to be a named export it would solve it... 馃

- "build": "rollup index.js --format=umd --name=ky --file=umd.js",
+ "build": "rollup index.js --format=umd --name=ky --file=umd.js --exports=named",

I'm pretty sure name creates a global in UMD and IIFE modes. Maybe I'm wrong, though. It's been a while since I looked at it.

Okay, I figured out what is going on here. The problem is not in the bundle itself, but rather our code example.

Try this:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd.js"></script>
<script>
(async () => {
    console.log(ky);
})();
</script>

You will see that, indeed, ky is available as a global.

Next, try this:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/umd.js"></script>
<script>
(async () => {
    const ky = 1234;
    console.log(ky);
})();
</script>

You will see that, indeed, you can _shadow_ the global ky variable. Meaning, you can initialize a ky variable in the lexical scope of the IIFE, even when using const, and it will simply take precedence over the global, within that scope.

So we have established that you can _use_ the global ky here and that you can _shadow_ the global ky here. But it turns out, you cannot do both at the same time! That is because of the temporal dead zone.

Take the original code example and change const ky = ky.default to const ky2 = ky.default and suddenly it will work.

My bad for not testing this properly when I implemented it. I tested the bundle and I tested the code example in an app, but I think I used a different variable name and then changed the variable to ky to fit in better with the docs, and I overlooked the temporal dead zone problem.

Thankfully, the fix is easy, and can be done in userland until our docs are updated. The question is, what should the docs do?

How about this?

const client = ky.default;

Or maybe our global should be something like _ky so that you can do:

const ky = _ky.default;

... but that feels a bit more ugly to me.

@sholladay thanks for looking into this. What an interesting edge case, I've learned a lot!

In terms of the solution, I think I would prefer to just change the IIFE variable name in the docs and not change the exposed global to _ky (also feels ugly to me).

I can put together a PR to update the docs if you want!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kirillgroshkov picture kirillgroshkov  路  3Comments

sarneeh picture sarneeh  路  6Comments

DanielRuf picture DanielRuf  路  4Comments

lkostrowski picture lkostrowski  路  7Comments

kahirokunn picture kahirokunn  路  3Comments