UNPKG

x3d-tidy

Version:

X3D Converter, Beautifier and Minimizer

235 lines (210 loc) 6.81 kB
"use strict"; const X3D = require ("x_ite-node"), pkg = require ("../package.json"), infer = require ("./infer"), metadata = require ("./metadata"), yargs = require ("yargs"), { hideBin } = require ("yargs/helpers"), url = require ("url"), path = require ("path"), fs = require ("fs"), zlib = require ("zlib"), DEBUG = false; main (); async function main () { try { await convert (); process .exit (); } catch (error) { console .error (error .message || error); process .exit (1); } } async function convert () { const args = yargs (hideBin (process .argv)) .scriptName ("x3d-tidy") .usage ("$0 [options] input-file output-file [input-file output-file ...]") .wrap (yargs () .terminalWidth ()) .command ("X3D converter, beautifier and minimizer") .version (pkg .version) .alias ("v", "version") .fail ((msg, error, yargs) => { console .error (msg); process .exit (1); }) .option ("double", { type: "number", alias: "d", description: "Set double precision, default is 15.", array: true, default: [15], }) .option ("float", { type: "number", alias: "f", description: "Set float precision, default is 7.", array: true, default: [7], }) .option ("input", { type: "string", alias: "i", description: "Set input file(s). If there are less input files than output files, the last input file is used for the remaining output files.", array: true, default: [ ], implies: "output", }) .option ("metadata", { type: "boolean", alias: "m", description: "If set, remove metadata nodes.", array: true, default: [false], }) .option ("output", { type: "string", alias: "o", description: `Set output file(s). To output it to stdout use only the extension, e.g. ".x3dv".`, array: true, default: [ ], implies: "input", }) .option ("infer", { type: "boolean", alias: "r", description: "If set, infer profile and components from used nodes.", array: true, default: [false], }) .option ("style", { type: "string", alias: "s", description: `Set output style, default is "TIDY". "TIDY" results in a good readable file, but with larger size, whereas "CLEAN" result in the smallest size possible by removing all redundant whitespaces. The other values are somewhere in between.`, choices: ["TIDY", "COMPACT", "SMALL", "CLEAN"], array: true, default: ["TIDY"], }) .example ([ [ "npx x3d-tidy -i file.x3d -o file.x3dv", "Convert an XML encoded file to a VRML encoded file." ], [ "npx x3d-tidy -s CLEAN -i file.x3d -o file.x3dv file.x3dj", "Convert an XML encoded file to a VRML encoded file and a JSON encoded file with smallest size possible by removing redundant whitespaces" ], ]) .help () .alias ("help", "h") .argv; // Fixes an issue with URL, if it matches a drive letter. args .input = args .input .map (input => input .replace (/^([A-Za-z]:)/, "file://$1")); const browser = X3D .createBrowser () .browser, scenes = new Map (); browser .setBrowserOption ("PrimitiveQuality", "HIGH"); browser .setBrowserOption ("TextureQuality", "HIGH"); browser .setBrowserOption ("LoadUrlObjects", false); browser .setBrowserOption ("Mute", true); browser .endUpdate (); await browser .loadComponents (browser .getProfile ("Full")); if (!args .input .length) console .warn ("No input files specified."); for (const i of args .output .keys ()) { const input = new URL (arg (args .input, i), url .pathToFileURL (path .join (process .cwd (), "/"))), scene = scenes .get (input .href) ?? await browser .createX3DFromURL (new X3D .MFString (input)), generator = scene .getMetaData ("generator") ?.filter (value => !value .startsWith (pkg .name)) ?? [ ]; scenes .set (input .href, scene); generator .push (`${pkg .name} V${pkg .version}, ${pkg .homepage}`); scene .setMetaData ("generator", generator); scene .setMetaData ("modified", new Date () .toUTCString ()); if (arg (args .infer, i)) infer (scene); if (arg (args .metadata, i)) metadata (scene); const options = { scene: scene, style: arg (args .style, i), precision: arg (args .float, i), doublePrecision: arg (args .double, i), }; const output = path .resolve (process .cwd (), args .output [i]); if (path .extname (output)) fs .writeFileSync (output, getContents ({ ... options, type: path .extname (output) })); else console .log (getContents ({ ... options, type: path .basename (output) })); } scenes .forEach (scene => scene .dispose ()); browser .dispose (); } function arg (arg, i) { return arg [i] ?? arg .at (-1); } function getContents ({ scene, type, style, precision, doublePrecision }) { switch (type .toLowerCase ()) { default: case ".x3d": return scene .toXMLString ({ style: style || "TIDY", precision, doublePrecision }); case ".x3dz": return zlib .gzipSync (scene .toXMLString ({ style: style || "CLEAN", precision, doublePrecision })); case ".x3dv": return scene .toVRMLString ({ style: style || "TIDY", precision, doublePrecision }); case ".x3dvz": return zlib .gzipSync (scene .toVRMLString ({ style: style || "CLEAN", precision, doublePrecision })); case ".x3dj": return scene .toJSONString ({ style: style || "TIDY", precision, doublePrecision }); case ".x3djz": return zlib .gzipSync (scene .toJSONString ({ style: style || "CLEAN", precision, doublePrecision })); case ".html": return getHTML (scene); } } function getHTML (scene) { return /* html */ `<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cdn.jsdelivr.net/npm/x_ite@latest/dist/x_ite.min.js"></script> <style> body { background-color: rgb(21, 22, 24); color: rgb(108, 110, 113); } a { color: rgb(106, 140, 191); } x3d-canvas { width: 768px; height: 432px; } </style> </head> <body> <h1>${path .basename (new URL (scene .worldURL) .pathname)}</h1> <x3d-canvas> ${scene .toXMLString ({ html: true, indent: " " .repeat (6) }) .trimEnd ()} </x3d-canvas> <p>Made with <a href="https://www.npmjs.com/package/x3d-tidy" target="_blank">x3d-tidy.</a></p> </body> </html>`; }