UNPKG

@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

175 lines (153 loc) 7.82 kB
import { createWriteStream, existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'; import path from 'path'; const projectDir = process.cwd() + "/"; /** these are alias callbacks as in the vite.alias dictionary * the first argument is the already resoled absolute path (it is only invoked if the path was found in node_modules) * the 2,3,4 args are the same as in vite.alias (packageName, index, path); */ /** * @type {Record<string, null | ((res: string, packageName: string, index: number, path: string) => string | null | void)>} */ const packages_to_resolve = { // Handle all previous imports where users did import using @needle-engine/src '@needle-tools/engine/src': (res, packageName, index, path) => { // resolve old engine/src imports UNLESS it's the asap plugin (the asap plugin currently only exists in the src folder) if (!path.startsWith("@needle-tools/engine/src/asap")) { return res + "/../lib"; } }, /* Removed the previously present @needle-tools/engine entry because this is automatically done by vite according to whatever we define in our package.json exports This did previously prevent us from declaring proper exports in package.json */ '@needle-tools/engine': (res, packageName, index, path) => { // Check if the import is something like @needle-tools/engine/engine/engine_utils // in which case we want to resolve into the lib directory if (path.startsWith("@needle-tools/engine/engine")) { return res + "/lib"; } }, /* Removed. Three.js is manually resolved below to ensure all dependencies resolve to the same three.js version. 'three': null, */ 'peerjs': null, 'websocket-ts': null, 'md5': null, } /** * @param {import('../types').userSettings} userSettings */ export const needleViteAlias = (command, config, userSettings) => { if (config?.noAlias === true || userSettings?.noAlias === true) return; const debug = userSettings.debugAlias; let outputDebugFile = null; function log(...msg) { console.log(...msg); if (debug && outputDebugFile) outputDebugFile.write(msg.join(" ") + "\n"); } if (debug) { const outputFilePath = path.resolve(projectDir, 'node_modules/.vite/needle.alias.log'); const outputDirectory = path.dirname(outputFilePath); if (!existsSync(outputDirectory)) mkdirSync(outputDirectory, { recursive: true }); outputDebugFile = createWriteStream(outputFilePath, { flags: "a" }); const timestamp = new Date().toISOString(); outputDebugFile.write("\n\n\n--------------------------\n"); outputDebugFile.write(`[needle-alias] Logging to: ${outputFilePath} (${timestamp})\n`); log("[needle-alias] Logging to: ", outputFilePath); } const aliasPlugin = { name: "needle-alias", config(config) { if (debug) console.log('[needle-alias] ProjectDirectory: ' + projectDir); if (!config.resolve) config.resolve = {}; if (!config.resolve.alias) config.resolve.alias = {}; const aliasDict = config.resolve.alias; addThreeJSResolvers(aliasDict); for (const name in packages_to_resolve) { if (!aliasDict[name]) { addPathResolver(name, aliasDict, packages_to_resolve[name]); } } if (debug) { const testResults = []; for (const name in aliasDict) { const entry = aliasDict[name]; let res = entry; if (typeof entry === "function") res = entry(name, 0, name); testResults.push({ name, entry: res }); } console.log('[needle-alias] Aliases: ', testResults); } }, } let lastImporter = ""; /** This plugin logs all imports. This helps to find cases where incorrect folders are found/resolved. */ /** @type {import("vite").Plugin} */ const debuggingPlugin = { name: "needle:alias-debug", // needs to run before regular resolver enforce: 'pre', resolveId(id, importer, options) { if (importer) { // simplify paths for better readability if (importer.includes("js/package~")) importer = "package~" + importer.split("js/package~")[1]; if (importer.includes("node_modules/@needle-tools")) importer = "node_modules/@needle-tools" + importer.split("node_modules/@needle-tools")[1]; if (importer.includes("node_modules/.vite")) importer = ".vite" + importer.split("node_modules/.vite")[1]; } // could filter here, e.g. for things related to three // if (id.includes("three")) return; // verbose logging for all imports if (lastImporter !== importer) { lastImporter = importer; log(`[needle-alias] Resolving: ${importer} (file${options?.ssr ? ", SSR" : ""})`); } log(`[needle-alias] → ${id}`); return; }, } if (debug) return [debuggingPlugin, aliasPlugin]; return [aliasPlugin]; function addThreeJSResolvers(aliasDict) { // We are currently overriding "three" resolution to ensure that all dependencies resolve to the same three.js version. // This is hacky, but the alternative is potentially having conflicting three.js versions since some packages are // stubborn with their peer dependencies or just slow (slower as we) with updating. // NOT adding this allows node.js to correctly resolve `exports` specified in three.js package.json; // since we're overriding resolution here we need to manually resolve the subset of exports that we use. aliasDict['three/addons'] = (res, packageName, index, path) => { return "three/examples/jsm"; }; aliasDict['three/nodes'] = (res, packageName, index, path) => { return "three/examples/jsm/nodes/Nodes.js"; }; aliasDict['three'] = (res, packageName, index, _path) => { return path.resolve(projectDir, 'node_modules', 'three'); }; } function addPathResolver(name, aliasDict, cb) { // If a package at the node_modules path exist we resolve the request there // introduced in 89a50718c38940abb99ee16c5e029065e41d7d65 const fullpath = path.resolve(projectDir, 'node_modules', name); if (typeof cb !== "function") cb = null; const isDevEnvironment = process.env.NODE_ENV === "development"; if (existsSync(fullpath)) { aliasDict[name] = (packageName, index, path) => { if (cb !== null && !isDevEnvironment) { const overrideResult = cb(fullpath, packageName, index, path); if (overrideResult !== undefined) if (existsSync(overrideResult)) { console.warn(`[needle-alias] \"${path}\" was requested and resolved to \"${overrideResult}\"`); return overrideResult; } } if (path != packageName) { // TODO: we might want to check if the package.json exports contains the path to see if it's valid console.warn(`[needle-alias] \"${path}\" was requested and resolved to \"${fullpath}\"`); } return fullpath; } } } };