How do you make your own (personal) library of scripts?

I have made a few functions to abstract some stuff that I plan on doing over and over in many different projects. Therefore I have my current project in one directory, but also the library of functions in a different directory. For example (the details of what’s in the library aren’t very important for my question, I don’t think, but if anyone wants to see more of the code, I can include it):

In lib\library.ts,

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

const black = new Color3(0,0,0);

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,-2,20) );
    camera.cameraDirection.set( 0,5,-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};
}

... (so on with more functions)

In \vite-project\src\proj.ts

import './style.css';
import {Engine, Scene, ArcRotateCamera, Vector3, Color3, HemisphericLight, MeshBuilder, AxesViewer, UniversalCamera, DynamicTexture, StandardMaterial} from "@babylonjs/core";
import {GridMaterial} from '@babylonjs/materials';
import {AdvancedDynamicTexture, GUI3DManager, HolographicSlate, Rectangle, TextBlock} from '@babylonjs/gui';

import {init, spinsphere, textbox, twodgrid} from "/Users/ax/BabylonScripts/main";

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

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

... (so on with making the rest of the app, making calls to functions written in library.ts)

Now I know that all the code works, since I can run the app and it looks how I intend, with no console errors. (By running yarn dev in the terminal.)

But when I try to build it, I think understandably, NPM complains.

/Users/ax/BabylonScripts/main.ts:3:51 - error TS2307: Cannot find module '@babylonjs/gui' or its corresponding type declarations.

3 import { AdvancedDynamicTexture, Rectangle } from '@babylonjs/gui';

The problem is that the library script lives outside of any project – which is what I want, because it shouldn’t be attached to any project. But it also calls on BabylonJS packages, which it can’t find, because it’s developed outside of … whatever it is. NPM, Yarn, Vite, whichever of those are responsible for “knowing” about the BabylonJS packages.

I’m guessing that I should “manually” make the library into an NPM package (i.e. in the \lib directory run npm init), and then in the package.json file, include BabylonJS as a dependency. So I did this and edited the file to look like this:

{
  "name": "babylonscripts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "dependencies": {
	  "@babylonjs/core": "^6.19.1",
	  "@babylonjs/gui": "^6.19.1"
  },
  "license": "ISC"
}

But this didn’t resolve the issue.

One slightly funny thing is that this is only a problem for @babylonjs/gui. I’m also using @babylonjs/core and @babylonjs/materials and neither of these show an error in intellisense or in the build process. Only the GUI library causes an error, both in intellisense and the build.


I guess also it only now occurs to me that I did all of this in the context of using Yarn and Vite, because I was following a YouTube walk-through. But the instructions in the documentation talk about Webpack. Maybe I should switch? I don’t know much about either of these, I’m mostly just doing what seems to be working for other people.

Thats a typescript error. Add skipLibCheck: true to your tsc config, will probably fix it. Wont fix it if you have declarations with lib. Tbh, imo its better to not emit declarations and just put a “types” field in your package json and point it to your source files. That way, you’re never missing any types in a monorepo and your code is the source of truth for types.

2 Likes

Am noob.

I don’t have declarations with lib, so I’m interpreting this as saying “You should have declarations, and you should set it up via the types field pointing to source files.” Big if true, I’ll look into what any of this means.

Thanks! :slight_smile:

Not suggesting you need to do either. just add skipLibCheck:true to your tsconfig first, it’ll prob fix it. Actually, id say just get comfortable with ts/js etc before you try to manage multiple projects finding each other on your drive. Copy paste works great. No need to make problems for yourself to solve.

3 Likes

Adding @RaananW our lib guru :slight_smile:

Small note - I would also recommend you to install @babylonjs/materials that you reference in some of the scripts you pasted.

On a larger note, as Jeremy said, it’s probably all because of your tsconfig . But it is hard to say without seeing the actual project code.

1 Like

Indeed! I’m actually not sure why it was working without that dependency … but at some point the compiler started complaining about the lack of the dependency, so I added it, and now it’s working.

Anyway, for now I actually have implemented a kind of stop-gap by just publishing an NPM package and then importing it. This feels kind of dumb, publishing a noob-level package just to get the build system to work the way I want it. But it is working in the intended ways – so at least I can continue to make progress.

After I’ve built a few more things, I’ll experiment with just doing what Jeremy said. It at least sounds smarter and more efficient. If that doesn’t work for whatever reason, I have also seen a few StackOverflow posts about how you can make local node packages, so that might work too.

Anyway, my question is basically answered, one way or another! I just don’t know exactly which answer I’m eventually going to live with, but I’m sure at least one of these solutions works.

Thanks one and all for the help!

Maybe it helps with one of your stated issues, but in vite.config.ts you can also add the @babylon packages as external dependencies e.g. here’s what we use for libraries:

import path from "path";
import { defineConfig } from "vite";
import dts from 'vite-plugin-dts'
import packageJson from "./package.json";

const getPackageName = () => {
  return packageJson.name;
};

const getPackageNameCamelCase = () => {
  try {
    return getPackageName().replace(/-./g, (char) => char[1].toUpperCase());
  } catch (err) {
    throw new Error("Name property in package.json is missing.");
  }
};

const fileName = {
  es: `${getPackageName()}.mjs`,
  cjs: `${getPackageName()}.cjs`,
  iife: `${getPackageName()}.iife.js`,
};

const formats = Object.keys(fileName) as Array<keyof typeof fileName>;

export default defineConfig({
  base: "./",
  build: {
    lib: {
      entry: path.resolve(__dirname, "src/index.ts"),
      name: getPackageNameCamelCase(),
      formats,
      fileName: (format) => fileName[format],
    },
    rollupOptions: {
      external: [
        "@babylonjs/core",
        "@babylonjs/inspector",
        "@babylonjs/loaders",
        "@babylonjs/materials",
        "@babylonjs/serializers",
      ],
    },
  },
  plugins: [dts({ rollupTypes: true })],
});