I am currently exploring what a complete external build pipeline looks like for fairly large-scale commercial PlayCanvas game projects. I recently made a post about combining TypeScript with ESM Scripts synced with playcanvas-sync. Cool!
Now I want to understand how I can create re-usable libraries for all my company’s games. I create each library as a separate Node.js package, keep them in a monorepo and publish them privately via GitHub Packages. I can then either add a dependency to the published library, or use npm link to symlink a local version if I also want to develop the library in parallel. All of this works now and I’m starting to understand how Node.js in particular works. Nice!
To make the libraries usable in a PlayCanvas-ready build, I use a bundler that resolves npm imports and emits ESM output with shared chunks for browser execution. This all looks good and seems to work.
However.
The bundler rewrites the module structure and emits the script class using an export list form (export { TestScript }) instead of a direct class export (export class TestScript extends Script).
PlayCanvas’ ESM script detection does not appear to register scripts unless the class is exported using the direct export class syntax.
If anyone tried anything similar it would be interesting to know if I’m doing it wrong, if you’ve run into similar problems. Maybe it’s a matter of an additional argument to my bundler to retain direct exports or just using a different bundler. I don’t think syntax-wise the output is wrong, but PlayCanvas doesn’t like it.
Original TS-file (the external library is not of interest in this example):
import {Script} from "playcanvas";
import {TestLibraryClass} from "@mikim-entertainment/test-library";
export class TestScript extends Script { // Note export syntax
static scriptName = "testScript";
initialize() {
const test = new TestLibraryClass();
}
}
And the bundler output, which is semantically equivalent ESM, but isn’t picked up by PlayCanvas ESM script detection:
import { TestLibraryClass } from "./chunk-QQOFT3CH.mjs";
// src/pc/test-script.ts
import { Script } from "playcanvas";
var TestScript = class extends Script { // Note export syntax
static scriptName = "testScript";
initialize() {
const test = new TestLibraryClass();
}
};
export { // Note export syntax
TestScript
};
//# sourceMappingURL=test-script.mjs.map
If I manually change the export syntax to this (below) it works, but I’d like to be able to use the bundler output without having to modify the files (obviously):
export class TestScript extends Script {
I’m using esbuild to bundle and pcwatch to sync to PlayCanvas. Is this a limitation of PlayCanvas’ current ESM script detection (possibly a bug or unimplemented case), or is there a bundler configuration or alternative bundler that preserves the export class form for entry modules?