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.

144 lines (123 loc) 5.9 kB
import path from 'path'; import fs from 'fs'; /** * @param {import('../types').userSettings} userSettings */ export const needleImportsLogger = (command, config, userSettings) => { if (!userSettings.debugImportChains) return; const graph = { allNodes: new Map(), graph: {}, }; const showOneLevelOfImportsThatWereAlreadyLogged = false; const showOneLevelOfImportedBy = true; const logToConsole = false; const logToImportsLogFile = true; return { name: 'needle:imports-logger', enforce: 'pre', resolveId(id, importer) { // we want to make a graph of all the imports // so when we get this call, // we can store the importer and id in a graph // to make this work properly we need to resolve the paths all to absolute paths // assuming that id is relative to importer if (importer) id = path.resolve(path.dirname(importer), id); if (!graph.allNodes.has(id)) { graph.allNodes.set(id, { id, imports: [], importedBy: [], }); } if (!graph.allNodes.has(importer)) { graph.allNodes.set(importer, { id: importer, imports: [], importedBy: [], }); } const node = graph.allNodes.get(id); const importerNode = graph.allNodes.get(importer); if (!node.importedBy.includes(importerNode)) node.importedBy.push(importerNode); if (!importerNode.imports.includes(node)) importerNode.imports.push(node); return; }, buildEnd(error) { try { // create log file and append lines to it const logFile = path.resolve(process.cwd(), "imports.log"); // append a single line efficiently; replace if necessary const fd = fs.openSync(logFile, "w"); const write = (str) => { if (logToConsole) console.log(str); if (logToImportsLogFile) { // replace coloring characters str = str.replace(/\x1b\[\d+m/g, ""); fs.writeSync(fd, str + "\n"); } } // log graph const loggedNodes = new Set(); const logNode = (node, depth, type) => { if (!showOneLevelOfImportsThatWereAlreadyLogged && loggedNodes.has(node.id)) return; const char = ["├", "├", "└"][type]; const depthStringWithExactlyThreePlaces = depth.toString().padStart(3, " "); let spacing = ("│ ").repeat(depth); // format console.log with dim grey let id = node.id; const alreadyLogged = loggedNodes.has(node.id); if (alreadyLogged) id = "\x1b[2m" + id + "\x1b[0m"; const suffix = node.imports.length > 0 ? " \x1b[2m[+" + node.imports.length + "]\x1b[0m" : ""; const lineString = "[" + depthStringWithExactlyThreePlaces + "]" + spacing; write(lineString + char + "─ " + id + (!showOneLevelOfImportsThatWereAlreadyLogged ? suffix : "")); if (showOneLevelOfImportedBy && node.importedBy.length > 0) { for (let i = 0; i < node.importedBy.length; i++) { const _importer = node.importedBy[i]; write(lineString + "│ " + "\x1b[2m^ " + _importer.id + "\x1b[0m"); } } if (alreadyLogged) return; loggedNodes.add(node.id); // all utf8 characters for showing a tree in a console: // ─ │ ├ └ ┌ ┐ ┘ ┴ ┬ ─ │ ├ └ ┌ ┐ ┘ ┴ ┬ // get list of nodes that haven't been displayed before // const childNodes = node.imports.filter(n => !loggedNodes.has(n.id)); const childNodes = node.imports; const omittedChildNodes = node.imports.length - childNodes.length; for( let i = 0; i < childNodes.length; i++) { const _import = childNodes[i]; const isFirst = i === 0; const isLast = i === childNodes.length - 1; logNode(_import, depth + 1, isLast ? 2 : isFirst ? 0 : 1); }; if (omittedChildNodes > 0) { const spacing = ("│ ").repeat(depth + 1); write("[" + depthStringWithExactlyThreePlaces + "]" + spacing + "└" + "─" + " " + omittedChildNodes + " more"); } } graph.allNodes.forEach(node => { try { logNode(node, 1, 0); } catch (e) { console.error(e); return; } }); fs.closeSync(fd); console.log(logFile + "(1,0) : [needle-imports-logger] Wrote Module Imports log file."); } catch (e) { console.error("Error in plugin", e); } } } }