@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
103 lines (87 loc) • 4.22 kB
JavaScript
import { copyFile, copyFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
import { getOutputDirectory } from './config.js';
const loadInstantGamesStr = `/* needle: injected to initialize facebook instant games */
let __progress = 0;
function onNeedleEngineInstantGamesLoadingProgress(_ctx, evt) {
FBInstant.setLoadingProgress(evt.detail.totalProgress01 * 100);
}
document.addEventListener("DOMContentLoaded", function () {
FBInstant.initializeAsync().then(function () { FBInstant.startGameAsync() });
});
`
// https://regex101.com/r/4ApFD9/1
function insertCallToOnProgress(html) {
const match = /(?<component><needle-engine.*?.*?>)/g.exec(html);
if (!match) {
log("!!! Could not find needle-engine component in index.html");
return html;
}
const progressEvtSignature = "progress=\"onNeedleEngineInstantGamesLoadingProgress\"";
const component = match.groups.component;
if (component.includes("progress")) return html;
log("Inserting progress callback event into needle-engine component")
const newComponent = component.replace(">", ` ${progressEvtSignature}>`);
return html.replace(component, newComponent);
}
function log(...any) {
console.log("[needle facebook instant games] ", ...any);
}
/**
* @param {{facebookInstantGames?:object}} config
* @param {import('../types').userSettings} userSettings
*/
export const needleFacebookInstantGames = (command, config, userSettings) => {
if (userSettings.noFacebookInstantGames === true) return;
// If the config is not present it means that we don't want to use fb instant games
if (!config || !(userSettings.facebookInstantGames || config.facebookInstantGames)) return;
/** @type {import('../types').userSettings["facebookInstantGames"]} */
const facebookInstantGamesConfig = { ...config.facebookInstantGames, ...userSettings.facebookInstantGames };
log("Setup Facebook Instant Games", facebookInstantGamesConfig);
// https://developers.facebook.com/docs/games/build/instant-games/reference/bundle-config
let bundleConfig = {}
const bundleConfigPath = process.cwd() + "/fbapp-config.json";
if (existsSync(bundleConfigPath)) {
log(`Found facebook bundle config exists at "${bundleConfigPath}" ...`);
bundleConfig = JSON.parse(readFileSync(bundleConfigPath, 'utf8'));
}
else {
log(`No facebook bundle config exists at "${bundleConfigPath}" - will generate one now`);
}
// Make sure the bundle has the required arguments:
if (!bundleConfig.instant_games) bundleConfig.instant_games = {}
const gamesConfig = bundleConfig.instant_games;
if (!gamesConfig.orientation) gamesConfig.orientation = "PORTRAIT";
if (!gamesConfig.override_web_orientation) gamesConfig.override_web_orientation = "LANDSCAPE";
if (!gamesConfig.navigation_menu_version) gamesConfig.navigation_menu_version = "NAV_BAR";
const outputDir = getOutputDirectory();
return {
name: 'needle:facebook-instant-games',
transformIndexHtml: {
order: 'post',
handler(html, _ctx) {
// post transform so we want to linebreak after the vite logs
console.log("\n");
writeFileSync(outputDir + "/fbapp-config.json", JSON.stringify(bundleConfig), 'utf8');
html = insertCallToOnProgress(html);
return {
html,
tags: [
{
tag: 'script',
attrs: {
src: 'https://connect.facebook.net/en_US/fbinstant.6.3.js',
async: true,
},
injectTo: 'head',
},
{
tag: 'script',
children: loadInstantGamesStr,
injectTo: 'body',
},
]
}
}
},
}
}