processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.4k stars 3.28k forks source link

p5.js + NextJS compilation error #6771

Open msanguineti opened 7 months ago

msanguineti commented 7 months ago

Most appropriate sub-area of p5.js?

p5.js version

1.9.0

Web browser and version

No response

Operating system

No response

Steps to reproduce this

Steps:

  1. add p5 to a nextjs 14.1.0 project
  2. create a simple canvas with a bouncing ball
  3. run npm run dev to start the development server
  4. the process is interrupted with an error:
$ next dev
   ▲ Next.js 14.1.1-canary.1
   - Local:        http://localhost:3000

 ✓ Ready in 2.2s
 ○ Compiling / ...
 ⨯ ./node_modules/p5/lib/p5.min.js
Module parse failed: Identifier 'o' has already been declared (7314:37)
|                             if (g ? x || (d = !0, w(), x = !0) : d = Boolean(o.first), t = Math.max(0, Math.floor(t)), r = Math.max(0, Math.floor(r)), d) {
|                                 if (!a) throw new Error("First frame must include a { palette } option");
>                                 var [o, h, f, p, m = 8] = [
|                                     v,
|                                     t,

Import trace for requested module:
./node_modules/p5/lib/p5.min.js
./app/p5-error.tsx

Snippet:

This is a fully working example on CodeSandbox which reproduces the problem:

https://codesandbox.io/p/sandbox/dry-tree-rnzjq9

PLEASE NOTE:

I have also raised this issue in the Next.JS repo:

https://github.com/vercel/next.js/issues/60897

welcome[bot] commented 7 months ago

Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, please make sure to fill out the inputs in the issue forms. Thank you!

lindapaiste commented 7 months ago

I don't have an answer but I do have a bit more info. It's something to do with the bundling of external dependencies, or possibly a problem with the dependency itself.

I wanted to see what part of the code that is and what variable o is before minification. It's in the loading_displaying file where we have two external dependencies omggif and gifenc.

The throw new Error on the line right above the compilation error is from the gifenc package here: https://github.com/mattdesl/gifenc/blob/64842fca317b112a8590f8fef2bf3825da8f6fe3/src/index.js#L85

The block of minified code is this:

function h(e = {}) {
    const {initialCapacity: t = 4096, auto: g = !0} = e, v = E(t);
    const b = new Uint8Array(256), j = new Int32Array(5003), _ = new Int32Array(5003);
    let x = !1;
    return {
        reset() {
            v.reset(), x = !1
        }, finish() {
            v.writeByte(a.trailer)
        }, bytes() {
            return v.bytes()
        }, bytesView() {
            return v.bytesView()
        }, get buffer() {
            return v.buffer
        }, get stream() {
            return v
        }, writeHeader: w, writeFrame(e, t, r, o = {}) {
            var {
                transparent: n = !1,
                transparentIndex: s = 0,
                delay: i = 0,
                palette: a = null,
                repeat: l = 0,
                colorDepth: u = 8,
                dispose: c = -1
            } = o;
            let d = !1;
            if (g ? x || (d = !0, w(), x = !0) : d = Boolean(o.first), t = Math.max(0, Math.floor(t)), r = Math.max(0, Math.floor(r)), d) {
                if (!a) throw new Error('First frame must include a { palette } option');
                var [o, h, f, p, m = 8] = [v, t, r, a, u];
                p = F(p.length) - 1, m = 128 | m - 1 << 4 | p, M(o, h), M(o, f), o.writeBytes([m, 0, 0]), T(v, a), 0 <= l && (p = v, h = l, p.writeByte(33), p.writeByte(255), p.writeByte(11), C(p, 'NETSCAPE2.0'), p.writeByte(3), p.writeByte(1), M(p, h), p.writeByte(0))
            }
            var y, f = Math.round(i / 10), o = v, m = c, l = f, h = n, p = s,
                i = (o.writeByte(33), o.writeByte(249), o.writeByte(4), p < 0 && (p = 0, h = !1), h = h ? (y = 1, 2) : y = 0, 0 <= m && (h = 7 & m), h <<= 2, o.writeByte(0 | h | y), M(o, l), o.writeByte(p || 0), o.writeByte(0), Boolean(a) && !d);
            c = v, n = t, s = r, y = i ? a : null, c.writeByte(44), M(c, 0), M(c, 0), M(c, n), M(c, s), y ? (n = F(y.length) - 1, c.writeByte(128 | n)) : c.writeByte(0), i && T(v, a), [l, o, s, n, c = 8, i, a, e] = [v, e, t, r, u, b, j, _], S(s, n, o, c, l, i, a, e)
        }
    };

The specific line which is a problem is

var [o, h, f, p, m = 8] = [v, t, r, a, u];

Where we are defining an o variable even though there is already an o variable in scope.

That code corresponds somehow to the encodeLogicalScreenDescriptor function? https://github.com/mattdesl/gifenc/blob/64842fca317b112a8590f8fef2bf3825da8f6fe3/src/index.js#L181C10-L201

function encodeLogicalScreenDescriptor(
  stream,
  width,
  height,
  palette,
  colorDepth = 8
) {
  const globalColorTableFlag = 1;
  const sortFlag = 0;
  const globalColorTableSize = colorTableSize(palette.length) - 1;
  const fields =
    (globalColorTableFlag << 7) |
    ((colorDepth - 1) << 4) |
    (sortFlag << 3) |
    globalColorTableSize;
  const backgroundColorIndex = 0;
  const pixelAspectRatio = 0;
  writeUInt16(stream, width);
  writeUInt16(stream, height);
  stream.writeBytes([fields, backgroundColorIndex, pixelAspectRatio]);
}

The variable o which is an argument of writeFrame is actually opts, the options object. And then we get another variable o which is unrelated, representing the stream argument of encodeLogicalScreenDescriptor. That variable should be scoped within that function, but it doesn't seem like there's a function in that part of the minified code? So I'm confused.

Seems like something went wrong with the minification of gifenc but I don't know how or why that happens.

msanguineti commented 7 months ago

Hello @lindapaiste, so me let me understand, you are providing the minified code in your library. The interesting thing is that importing p5 in NextJS 14.0.4 is perfectly fine. When I have updated to 14.1.0 is when the problem started.

If nothing has changed on your side, the guys over at nextjs might have done some work on the bundler which has highlighted this problem. Unfortunately the issue I've opened there did gather interest from users that encountered this problem but not from the developers.

davepagurek commented 7 months ago

I'm not sure this is actually an error in the minification of p5, it's certainly not conventional code that a human would write, but I think it's valid to re-declare a variable in a different scope? e.g.:

function test(a) {
  console.log(a)
  var a = 2
  console.log(a)
}
test(1)
/* Logs:
1
2
*/

I also ran into this issue and had some luck also just using a different nextjs version (13.5.6 broke for me but 13.5.1 didn't.) I think this is probably just a bug in swc that they use for compilation, and seemingly has something to do with some other internal state that changes from version to version?

limzykenneth commented 6 months ago

If it helps, a quick test will be to turn off all optimizations (ie. minification) to see if the problem did stem from there. I don't use NextJS myself so it will take a bit more digging for me to know what's going on here.

Also generally using p5.js currently through npm installs are not tested and cannot be guaranteed to work.