diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d803281..a0efe3b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### ✨ Enhancements - Created a CLI for MemoryViz. +- Added `--height` and `--width` options to MemoryViz CLI. ### 🐛 Bug fixes @@ -42,6 +43,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Removed unused imports in `demo_C.js`. - Added type interfaces and type annotations to `style.ts`. - Added `DrawnEntity` type annotations to source code files. +- Adopted Commander.js library for the MemoryViz CLI. - Added `autofix.ci` to the CI workflow. ## [0.1.0] - 2024-04-16 diff --git a/docs/docs/06-cli.md b/docs/docs/06-cli.md index 294ea486..1d7f5e37 100644 --- a/docs/docs/06-cli.md +++ b/docs/docs/06-cli.md @@ -16,6 +16,8 @@ $ npx memory-viz replacing `` with the path to a file containing MemoryViz-compatible JSON. If the file content is not compatible with MemoryViz, an error will be thrown. +Optional arguments `--height` and `--width` can be used to specify the height and width of the generated SVG. + ## Output The output is an SVG image generated by MemoryViz and the image is saved in the current working directory. The name of the SVG will be the same as that of the inputted file (i.e., if the inputted file is `david-is-cool.json`, the output will be `david-is-cool.svg`). diff --git a/memory-viz/bin/cli.js b/memory-viz/bin/cli.js index 257faed6..47b2a6a4 100644 --- a/memory-viz/bin/cli.js +++ b/memory-viz/bin/cli.js @@ -3,15 +3,22 @@ const fs = require("fs"); const path = require("path"); const { draw } = require("memory-viz"); +const { program } = require("commander"); -// Check for correct number of arguments -if (process.argv.length !== 3) { - console.error( - "Error: wrong number of arguments.\nUsage: memory-viz " - ); - process.exit(1); -} -const filePath = process.argv[2]; +program + .description( + "Command line interface for generating memory model diagrams with MemoryViz" + ) + .argument( + "", + "path to a file containing MemoryViz-compatible JSON" + ) + .option("--width ", "width of generated SVG", "1300") + .option("--height ", "height of generated SVG"); + +program.parse(); + +const filePath = program.args[0]; const absolutePath = path.resolve(process.cwd(), filePath); // Checks if absolutePath exists and that it is a JSON file @@ -33,9 +40,10 @@ try { let m; try { - // TODO: Replace width and seed with command-line arguments + // TODO: Replace seed with command-line arguments m = draw(data, true, { - width: 1300, + width: program.opts().width, + height: program.opts().height, roughjs_config: { options: { seed: 12345 } }, }); } catch (err) { diff --git a/memory-viz/package.json b/memory-viz/package.json index 3079c1c7..e769dd8e 100644 --- a/memory-viz/package.json +++ b/memory-viz/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "@xmldom/xmldom": "^0.8.6", + "commander": "^12.1.0", "deepmerge": "^4.3.1", "roughjs": "^4.5.0" }, @@ -54,6 +55,7 @@ "babel-loader": "^9.1.0", "husky": "^8.0.3", "jest": "^29.7.0", + "tmp": "^0.2.3", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "typescript": "^5.3.3", diff --git a/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap b/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap index 850f7f79..3c1851f7 100644 --- a/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap +++ b/memory-viz/src/tests/__snapshots__/cli.spec.tsx.snap @@ -1,3 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`memory-viz cli produces consistent svg when provided height and width options 1`] = `""David is cool!"id19str"`; + +exports[`memory-viz cli produces consistent svg when provided height option 1`] = `""David is cool!"id19str"`; + +exports[`memory-viz cli produces consistent svg when provided width option 1`] = `""David is cool!"id19str"`; + exports[`memory-viz cli should produce an svg that matches snapshot 1`] = `""David is cool!"id19str"`; diff --git a/memory-viz/src/tests/cli.spec.tsx b/memory-viz/src/tests/cli.spec.tsx index cbd6570b..eea7f45a 100644 --- a/memory-viz/src/tests/cli.spec.tsx +++ b/memory-viz/src/tests/cli.spec.tsx @@ -5,22 +5,22 @@ const tmp = require("tmp"); tmp.setGracefulCleanup(); +const tmpFile = tmp.fileSync({ postfix: ".json" }); +const filePath = tmpFile.name; +const input = JSON.stringify( + [ + { + type: "str", + id: 19, + value: "David is cool!", + style: ["highlight"], + }, + ], + null +); + describe("memory-viz cli", () => { it("should produce an svg that matches snapshot", (done) => { - const tmpFile = tmp.fileSync({ postfix: ".json" }); - const filePath = tmpFile.name; - const input = JSON.stringify( - [ - { - type: "str", - id: 19, - value: "David is cool!", - style: ["highlight"], - }, - ], - null - ); - fs.writeFileSync(filePath, input); exec(`memory-viz ${filePath}`, (err) => { @@ -35,16 +35,61 @@ describe("memory-viz cli", () => { done(); }); }); + + it("produces consistent svg when provided width option", (done) => { + fs.writeFileSync(filePath, input); + + exec(`memory-viz ${filePath} --width=700`, (err) => { + if (err) throw err; + const svgFilePath = path.resolve( + process.cwd(), + path.basename(filePath.replace(".json", ".svg")) + ); + const fileContent = fs.readFileSync(svgFilePath, "utf8"); + expect(fileContent).toMatchSnapshot(); + fs.unlinkSync(svgFilePath); + done(); + }); + }); + + it("produces consistent svg when provided height option", (done) => { + fs.writeFileSync(filePath, input); + + exec(`memory-viz ${filePath} --height=700`, (err) => { + if (err) throw err; + const svgFilePath = path.resolve( + process.cwd(), + path.basename(filePath.replace(".json", ".svg")) + ); + const fileContent = fs.readFileSync(svgFilePath, "utf8"); + expect(fileContent).toMatchSnapshot(); + fs.unlinkSync(svgFilePath); + done(); + }); + }); + + it("produces consistent svg when provided height and width options", (done) => { + fs.writeFileSync(filePath, input); + + exec(`memory-viz ${filePath} --height=700 width=1200`, (err) => { + if (err) throw err; + const svgFilePath = path.resolve( + process.cwd(), + path.basename(filePath.replace(".json", ".svg")) + ); + const fileContent = fs.readFileSync(svgFilePath, "utf8"); + expect(fileContent).toMatchSnapshot(); + fs.unlinkSync(svgFilePath); + done(); + }); + }); }); describe.each([ { errorType: "invalid arguments", command: "memory-viz", - expectedErrorMessage: - `Command failed: memory-viz\n` + - `Error: wrong number of arguments.\n` + - `Usage: memory-viz \n`, + expectedErrorMessage: "error: missing required argument 'filepath'", }, { errorType: "non-existent file", diff --git a/package-lock.json b/package-lock.json index 864cb09b..9ace7675 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,9 @@ "demo", "docs" ], + "dependencies": { + "commander": "^12.1.0" + }, "devDependencies": { "husky": "^8.0.3", "lint-staged": "^14.0.1", @@ -6391,11 +6394,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -7209,9 +7212,12 @@ } }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } }, "node_modules/common-path-prefix": { "version": "3.0.0", @@ -9281,9 +9287,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -13254,9 +13260,9 @@ } }, "node_modules/jsdom/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -19592,6 +19598,11 @@ } } }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -20916,9 +20927,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.15.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", - "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -21229,9 +21240,9 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "engines": { "node": ">=8.3.0" }, diff --git a/package.json b/package.json index deb39e9c..7f7094d8 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "devDependencies": { "husky": "^8.0.3", "lint-staged": "^14.0.1", - "prettier": "2.8.1", - "tmp": "^0.2.3" + "prettier": "2.8.1" }, "lint-staged": { "**/*": "prettier --write --ignore-unknown"