`Uncaught SyntaxError: ambiguous indirect export` with noncircular imports

446 Views Asked by At

An update to the problem

Here is something new that has only occurred about an hour after the problem began: The problem has completely disappeared.

Now this has happened once before. I had previously edited the babylon-helper-lib package, updated my project, built, and it gave me this SyntaxError. I tinkered with it, nothing worked. But after a while, it started working mysteriously. I assumed that maybe I had removed some import that made it start working again.

But this time I didn't tinker with it at all. In fact, I built it, restarted the browser, restarted the server, upgraded all NPM packages, so on. I did as much as I could to make sure that the build was totally fresh. Error still occurs.

An hour later, reboot everything just to see what happens. No changes to anything at all. Now the error is gone and everything works as intended.

On the one hand, great, it's working. On the other hand, this builds in an extra hour of waiting every time I have to go through this. So if anyone has any guesses about what's going on here, I'd love to hear it!


The error

Uncaught SyntaxError: ambiguous indirect export: arrowBuilder

What I think is relevant

At the end of this post I'll show the full code, in case it's helpful. But for now I'll just indicate the lines of code that I think are relevant.


In trans.ts I have the following:

import './style.css';
import { Space, Vector3, TransformNode } from '@babylonjs/core';
import { TextBlock, Button, AdvancedDynamicTexture, Control } from '@babylonjs/gui';

import { init, spinsphere, getAngleBetween, arrowBuilder } from "babylon-helper-lib";

...

and this injects a 3D app inside of index.html which contains

  <div>
    <canvas id="p3" class="app"></canvas>
  </div>

<script type="module" src="/src/trans.ts"></script>

The NPM package babylon-helper-lib is one that I made and published. It has the file lib.ts containing:

import { ArcRotateCamera, Color3, Engine, HemisphericLight, Mesh, MeshBuilder, Scene, Vector3, Space, StandardMaterial } from "@babylonjs/core";
import { GridMaterial } from "@babylonjs/materials";
import { AdvancedDynamicTexture, Rectangle } from "@babylonjs/gui";

type ArrowParams = {
    startx?: number;
    starty?: number;
    startz?: number;
    endx?: number;
    endy?: number;
    endz?: number;
    dirx?: number;
    diry?: number;
    dirz?: number;
    thick?: number;
    colorr?: number;
    colorg?: number;
    colorb?: number;
}
export let arrowBuilder = function( scene: Scene, params: ArrowParams ) {
    ...
}

and it exports the file index.ts containing

export { init, 
         twodgrid, 
         spinsphere, 
         textbox, 
         planeFromPtVec,
         getAngleBetween,
         arrowBuilder } from "./lib";

What I've tried

This page enumerates a number of fixes.

SyntaxError: ambiguous indirect export: default Error when importing my own class

Most of them don't apply to my situation, and some that do, I've tried to pursue but it hasn't fixed anything. For instance, one person's issue is that they weren't using the most updated packages from NPM, but I have made sure to call npm upgrade and it reports that everything is up-to-date. I've also edited the version numbers in package.json for both my web page and for babylon-helper-lib to be sure that both are the most recent versions. There seem to be no errors along these lines.

I've also seen another post talking about circular imports. I don't think I have any circular imports.

However, what I do seem to have are imports both in babylon-helper-lib and my web page, importing the same thing from BabylonJS like for instance Vector3. If I had to guess, I would guess that this is the source of the problem. But (1) this is much closer to a guess than a belief. And (2) if this is the problem, I'm not sure how to resolve it because both pieces of software need to import the same objects.


The full code

trans.ts

import './style.css';
import { Space, Vector3, TransformNode } from '@babylonjs/core';
import { TextBlock, Button, AdvancedDynamicTexture, Control } from '@babylonjs/gui';

import { init, spinsphere, textbox, twodgrid, planeFromPtVec, getAngleBetween, arrowBuilder } from "babylon-helper-lib";

// P1

const p1bag = init("#p1");
const p1scene = p1bag.scene;

twodgrid(p1scene);
spinsphere(p1scene, 0,0,.1);
spinsphere(p1scene, 1,1,.1, 0,1,0, .2, true);
spinsphere(p1scene, 2,1,.1, 0,0,1);

const yellow = "rgb(200,200,100)";
textbox(20,.2,0,0,.11,yellow);
textbox(.2,20,0,0,.11,yellow);

const tbx = textbox(1,1,7,-.4,1);
const xtext = new TextBlock();
xtext.text = "x";
xtext.color = "green";
xtext.fontSize = 36;
tbx.container.addControl(xtext);

const tby = textbox(1,1,-.4,7,1);
const ytext = new TextBlock();
ytext.text = "y";
ytext.color = "green";
ytext.fontSize = 36;
tby.container.addControl(ytext);

var tickbag;
var tick;
for (let i=-5; i<6; i++) {
    tickbag = textbox(.4,.4,i,-.4,.2);
    tick = new TextBlock();
    tick.text = i.toString();
    tick.color = "white";
    tick.fontSize = 36;
    tickbag.container.addControl(tick);

    tickbag = textbox(.3,.3,-.4,i,.2);
    tickbag.container.addControl(tick);
}

p1bag.engine.runRenderLoop( () => {
    p1scene.render();
});

// P2

// Set the equipment.
const p2bag = init("#p2");
const p2scene = p2bag.scene;
p2bag.camera.upVector = new Vector3(0,0,1);
p2bag.camera.position = new Vector3(5,5,3);


// Axis planes.
planeFromPtVec(p2scene);
planeFromPtVec(p2scene, 0,0,0, 1,0,0);

// Spinning spheres.
const s1 = spinsphere(p2scene, 1,0,0, 1,0,0).sphere;
const s2 = spinsphere(p2scene, 0,1,0, 0,1,0, .2, true).sphere;
const s3 = spinsphere(p2scene, 0,0,1, 0,0,1).sphere;

// Hidden copies 
const pivot = new TransformNode("root");
pivot.position = Vector3.Zero();
const r1 = s1.clone();
const r2 = s2.clone();
const r3 = s3.clone();
r1.parent = pivot;
r2.parent = pivot;
r3.parent = pivot;

// 0 means spheres overlap
// 1 means spheres are rotating
// 2 means spheres are at the end of rotation
var state = 0;

// Button
const button = Button.CreateSimpleButton("rotbtn", "rotate");
const texture = AdvancedDynamicTexture.CreateFullscreenUI("ui");
button.width = "200px";
button.height = "50px";
button.color = "white";
button.background = "deepskyblue";
button.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT;
button.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;

button.onPointerDownObservable.add( function() {
    if (state > 0) {
        r1.setAbsolutePosition(s1.position.clone());
        r2.setAbsolutePosition(s2.position.clone());
        r3.setAbsolutePosition(s3.position.clone());
        state = 0;
    } else {
        state = 1;
    }
});
texture.addControl(button);

const rotax = new Vector3(0,0,1);

var ang = 0;
var abspos = r1.getAbsolutePosition();
p2bag.engine.runRenderLoop( () => {
    p2scene.render();
    p2bag.camera.alpha += .001;
    if (state == 1) {
        pivot.rotate(rotax, .1, Space.WORLD);
        ang = Math.abs(getAngleBetween(abspos,s1.position));
        if (ang > 1) {
            state = 2;
        }
    }
});

// P3

const p3bag = init("#p3");
const p3scene = p3bag.scene;

arrowBuilder(p3scene, {startx:1, starty:1, startz:2, dirx:-1, diry:-1, dirz:-1, thick:.2});
spinsphere(p3scene, 1,0,0);
spinsphere(p3scene, 0,1,0, 0,1,0, .2, true);
spinsphere(p3scene, 0,0,1, 0,0,1);

p3bag.engine.runRenderLoop( () => {
    p3scene.render();
});

index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + TS</title>
  </head>
  <body>

<div id="pgrid">
  <div>
    <canvas id="p1" class="app"></canvas>
  </div>
  <div>
    <canvas id="p2" class="app"></canvas>
  </div>
  <div>
    <canvas id="p3" class="app"></canvas>
  </div>
</div>


<script type="module" src="/src/trans.ts"></script>
  </body>
</html>

And then in babylon-helper-lib I have a file lib.ts

import { ArcRotateCamera, Color3, Engine, HemisphericLight, Mesh, MeshBuilder, Scene, Vector3, Space, StandardMaterial } from "@babylonjs/core";
import { GridMaterial } from "@babylonjs/materials";
import { AdvancedDynamicTexture, Rectangle } from "@babylonjs/gui";

export let init = function(canvasId: string, width="500px", height="500px") {
    const canvas = document.querySelector<HTMLCanvasElement>(canvasId);
    if (canvas == null) {throw new Error("canvas id not found");}

    canvas.style.width = width;
    canvas.style.height = height;

    const engine = new Engine(canvas,true);
    const scene = new Scene(engine);

    scene.useRightHandedSystem = true;

    const camera = new ArcRotateCamera("Camera", 0,0,1, Vector3.Zero(), scene);
    camera.attachControl(canvas, true);
    camera.setPosition( new Vector3(0,-4,20) );
    camera.cameraDirection.set( 0,7,-20 );

    const light = new HemisphericLight("light", new Vector3(.5,1,0), scene);
    light.intensity = .7;
    scene.ambientColor = new Color3(.1,.2,.3);

    return {canvas, engine, scene, camera, light};
}

export let twodgrid = function(scene: Scene, width=20, height=20, name="box") {
    const box = MeshBuilder.CreateBox(name, {width:width,height:height,depth:.2});
    
    const boxmat = new GridMaterial(`${name}_mat`, scene);
    boxmat.majorUnitFrequency = 2;
    boxmat.useMaxLine = false;
    
    box.material = boxmat;

    return {box, boxmat};
}

export let spinsphere = function(scene:Scene, x:number,y:number,z=0, 
                                 red=1,green=0,blue=0, radius=.2, 
                                 whiteLines=false, spin=true) {
    const sphere = MeshBuilder.CreateSphere("sphere", {diameter:radius*2});
    
    const spheremat = new GridMaterial("spheremat", scene);
    spheremat.mainColor = new Color3(red,green,blue);
    
    sphere.material = spheremat;
    sphere.position.set(x,y,z);

    if (whiteLines) { spheremat.lineColor = new Color3(1,1,1); }

    if (spin) {
        scene.registerBeforeRender( function() {
            sphere.rotate(new Vector3(0,1,1), .01);
        });
    }

    return {sphere, spheremat};
}

export let textbox = function(width:number,height:number, 
                              x=0,y=0,z=0, 
                              color="black") {
    const plane = MeshBuilder.CreatePlane( "plane", {width:width,height:height} );
    plane.position.set(x,y,z);

    const texture = AdvancedDynamicTexture.CreateForMesh(plane, 30,50);
    
    const container = new Rectangle();
    container.thickness = .1;
    container.background = color;
    container.alpha = .7;

    texture.addControl(container);
    return {plane,texture,container};
}

export let planeFromPtVec = function(scene: Scene, ox=0,oy=0,oz=0, vx=0,vy=0,vz=1) {
    const plane = MeshBuilder.CreatePlane("PLANE", {size:10, sideOrientation:Mesh.DOUBLESIDE}, scene);
    
    // The normal to the plane is <0,0,1>.  Rotate it to the desired normal by 
    // first finding their cross: <0,0,1> x <vx,vy,vz> = <-vy, vx, 0>.
    const axis = new Vector3(-vy, vx, 0);
    // Use this to rotate through the appropriate angle.
    const angle = Math.acos( vz/Math.sqrt(vx*vx+vy*vy+vz*vz) );
    plane.rotate(axis, angle, Space.WORLD);

    plane.position.set(ox,oy,oz);

    const planemat = new GridMaterial("planemat", scene);
    planemat.lineColor = new Color3(1,1,1);
    planemat.mainColor = new Color3(1,1,1);
    planemat.opacity = .9;
    plane.material = planemat;

    return {plane,planemat}
}

export let getAngleBetween = function(v1: Vector3, v2: Vector3) {
    const ctheta = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/(v1.length()*v2.length());
    return ( Math.acos(ctheta) );
}

type ArrowParams = {
    startx?: number;
    starty?: number;
    startz?: number;
    endx?: number;
    endy?: number;
    endz?: number;
    dirx?: number;
    diry?: number;
    dirz?: number;
    thick?: number;
    colorr?: number;
    colorg?: number;
    colorb?: number;
}
export let arrowBuilder = function( scene: Scene, params: ArrowParams ) {
    const start = [params.startx, params.starty, params.startz];
    const end = [params.endx, params.endy, params.endz];
    const dir = [params.dirx, params.diry, params.dirz];

    let all = function(a: (number|undefined)[]) {
        for (let i=0; i<a.length; i++) {
            if (a[i] == undefined) {
                return 0;
            }
        }
        return 1;
    }
    const s = all(start)+all(end)+all(dir);

    if ( s == 3 ) { throw new Error("Arrow start&end&dir defined -- must define precisely two"); }
    if ( s<2 ) { throw new Error("Arrow <2 defined -- must define precisely two"); }

    if (all(start) && all(dir)) {
        for (let i=0; i<2; i++) {
            end[i] = (start[i] || 0) + (dir[i] || 0);
        }
    }
    if (all(end) && all(dir)) {
        for (let i=0; i<2; i++) {
            start[i] = (end[i] || 0) - (dir[i] || 0);
        }
    }
    if (all(start) && all(end)) {
        for (let i=0; i<2; i++) {
            dir[i] = (end[i] || 0) - (start[i] || 0);
        }
    }

    const thicknum = (params.thick || 1);
    const rnum = (params.colorr || 1);
    const gnum = (params.colorg || .5);
    const bnum = (params.colorb || .5);
    const dv = new Vector3(dir[0]||0, dir[1]||0, dir[2]||0);
    const sv = new Vector3(start[0]||0, start[1]||0, start[2]||0);

    const shaft = MeshBuilder.CreateCylinder("shaft", {height:dv.length()*4/5, diameter:thicknum}, scene);
    const head = MeshBuilder.CreateCylinder("head", {height:dv.length()/5, diameterBottom:1.5*thicknum, diameterTop:0}, scene);
    head.position.set(0, dv.length()/2 , 0);
    const arrow = Mesh.MergeMeshes([shaft,head]);
    if (arrow == null) { throw new Error("Arrow null. "); }
    const arrowmat = new StandardMaterial("arrowmat", scene);
    arrowmat.ambientColor = new Color3(rnum, gnum, bnum);
    arrow.material = arrowmat;

    const rotax = new Vector3(dir[2]||0, 0, -(dir[0]||0));
    const angle = getAngleBetween(new Vector3(0,1,0), dv);
    arrow.rotate(rotax, angle, Space.WORLD);

    arrow.position = sv.add(dv.scale(.5));
    return {arrow,arrowmat};
}

and index.ts

export { init, 
         twodgrid, 
         spinsphere, 
         textbox, 
         planeFromPtVec,
         getAngleBetween,
         arrowBuilder } from "./lib";

Of course all of these projects have tons of long config files. I'm happy to share them, but the post already has tons of code in it so I'll hold off on those until they're requested.

0

There are 0 best solutions below