playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
119 lines (118 loc) • 4.04 kB
JavaScript
import { platform } from "../../core/platform.js";
import { Debug } from "../../core/debug.js";
import { script } from "../script.js";
import { ScriptTypes } from "../script/script-types.js";
import { registerScript } from "../script/script-create.js";
import { ResourceLoader } from "./loader.js";
import { ResourceHandler } from "./handler.js";
import { Script } from "../script/script.js";
const toLowerCamelCase = (str) => str[0].toLowerCase() + str.substring(1);
class ScriptHandler extends ResourceHandler {
/**
* Create a new ScriptHandler instance.
*
* @param {AppBase} app - The running {@link AppBase}.
* @ignore
*/
constructor(app) {
super(app, "script");
this._scripts = {};
this._cache = {};
}
clearCache() {
for (const key in this._cache) {
const element = this._cache[key];
const parent = element.parentNode;
if (parent) {
parent.removeChild(element);
}
}
this._cache = {};
}
load(url, callback) {
if (typeof url === "string") {
url = {
load: url,
original: url
};
}
const self = this;
script.app = this._app;
const onScriptLoad = (url.load, (err, url2, extra) => {
if (!err) {
const obj = {};
for (let i = 0; i < ScriptTypes._types.length; i++) {
obj[ScriptTypes._types[i].name] = ScriptTypes._types[i];
}
ScriptTypes._types.length = 0;
callback(null, obj, extra);
const urlWithoutEndHash = url2.split("&hash=")[0];
delete self._loader._cache[ResourceLoader.makeKey(urlWithoutEndHash, "script")];
} else {
callback(err);
}
});
const [basePath] = url.load.split("?");
const isEsmScript = basePath.endsWith(".mjs");
if (isEsmScript) {
this._loadModule(basePath, onScriptLoad);
} else {
this._loadScript(url.load, onScriptLoad);
}
}
open(url, data) {
return data;
}
patch(asset, assets) {
}
_loadScript(url, callback) {
const head = document.head;
const element = document.createElement("script");
this._cache[url] = element;
element.async = false;
element.addEventListener("error", (e) => {
callback(`Script: ${e.target.src} failed to load`);
}, false);
let done = false;
element.onload = element.onreadystatechange = function() {
if (!done && (!this.readyState || (this.readyState === "loaded" || this.readyState === "complete"))) {
done = true;
callback(null, url, element);
}
};
element.src = url;
head.appendChild(element);
}
_loadModule(url, callback) {
const isBrowserWithOrigin = platform.browser && window.location.origin !== "null";
const baseUrl = isBrowserWithOrigin ? window.location.origin + window.location.pathname : import.meta.url;
const importUrl = new URL(url, baseUrl);
import(
/* @vite-ignore */
/* webpackIgnore: true */
importUrl.toString()
).then((module) => {
const filename = importUrl.pathname.split("/").pop();
const scriptSchema = this._app.assets.find(filename, "script")?.data?.scripts;
for (const key in module) {
const scriptClass = module[key];
const extendsScriptType = scriptClass.prototype instanceof Script;
if (extendsScriptType) {
const lowerCamelCaseName = toLowerCamelCase(scriptClass.name);
if (!scriptClass.scriptName) {
Debug.warnOnce(`The Script class "${scriptClass.name}" must have a static "scriptName" property: \`${scriptClass.name}.scriptName = "${lowerCamelCaseName}";\`. This will be an error in future versions of PlayCanvas.`);
}
const scriptName = scriptClass.scriptName ?? lowerCamelCaseName;
registerScript(scriptClass, scriptName);
if (scriptSchema) this._app.scripts.addSchema(scriptName, scriptSchema[scriptName]);
}
}
callback(null, url, null);
}).catch((err) => {
callback(err);
});
}
}
export {
ScriptHandler
};