diff --git a/.c8rc.json b/.c8rc.json index d9fdc011..fd3cae12 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -11,14 +11,14 @@ ".mocharc.js", "**/*.d.ts", "**/*.d.mts", - "bin/*", "coverage/**/*", "dist/**/*", "doc/**/*", "docs/**/*", "public/**/*", "src/**/*-parser.mjs", - "src/**/*.template.js" + "src/**/*.template.js", + "src/cli/main.mts" ], "reporter": ["text-summary", "lcov", "html", "json-summary"] } diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml index 3a7607cb..3b64de99 100644 --- a/.github/codeql/codeql-config.yml +++ b/.github/codeql/codeql-config.yml @@ -1,10 +1,9 @@ -ame: 'CodeQL config' +ame: "CodeQL config" # See https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs paths: - src/ - - bin/ - tools/ - docs/sitesrc/ - docs/smcat-online-interpreter.js @@ -12,10 +11,10 @@ paths: - docs/index.html - docs/inpage.html paths-ignore: - - '**/*.template.js' - - '**/*-parser.mjs' + - "**/*.template.js" + - "**/*-parser.mjs" queries: - uses: security-and-quality query-filters: - exclude: - id: js/useless-assignment-to-local \ No newline at end of file + id: js/useless-assignment-to-local diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 32488de3..930927d4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -8,7 +8,6 @@ on: - main paths: - src/ - - bin/ - tools/ - docs/sitesrc/ - docs/smcat-online-interpreter.js @@ -20,7 +19,6 @@ on: - main paths: - src/ - - bin/ - tools/ - docs/sitesrc/ - docs/smcat-online-interpreter.js diff --git a/bin/smcat.mjs b/bin/smcat.mjs deleted file mode 100755 index b68ce61e..00000000 --- a/bin/smcat.mjs +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env node -// @ts-check -import executeCommandLine from "../dist/cli/execute-command-line.mjs"; - -await executeCommandLine(); diff --git a/config/dependency-cruiser/graph.mjs b/config/dependency-cruiser/graph.mjs index 16959c57..1c2e1e69 100644 --- a/config/dependency-cruiser/graph.mjs +++ b/config/dependency-cruiser/graph.mjs @@ -6,10 +6,10 @@ export default { options: { ...baseConfig.options, cache: "node_modules/.cache/dependency-cruiser/graph", - includeOnly: "^(bin|src)/", + includeOnly: "^src/", reporterOptions: { archi: { - collapsePattern: "^(bin|src/(cli|transform|[^/]+/[^/]+))", + collapsePattern: "^(src/(cli|transform|[^/]+/[^/]+))", }, dot: { theme: { diff --git a/config/dependency-cruiser/lint-staged.mjs b/config/dependency-cruiser/lint-staged.mjs index d61795f8..58e4a1fa 100644 --- a/config/dependency-cruiser/lint-staged.mjs +++ b/config/dependency-cruiser/lint-staged.mjs @@ -7,7 +7,7 @@ export default { // This configuration ensures dependency-cruiser will skip these // rules { name: "no-orphans", severity: "ignore" }, - { name: "no-unreachable-from-bin", severity: "ignore" }, + { name: "no-unreachable-from-cli", severity: "ignore" }, { name: "no-unreachable-from-api", severity: "ignore" }, { name: "no-uncovered-by-tests", severity: "ignore" }, ], diff --git a/config/dependency-cruiser/rules.mjs b/config/dependency-cruiser/rules.mjs index df476554..df2e25fe 100644 --- a/config/dependency-cruiser/rules.mjs +++ b/config/dependency-cruiser/rules.mjs @@ -58,7 +58,6 @@ export default { from: {}, to: { dependencyTypes: ["deprecated"], - pathNot: "node_modules/viz\\.js/viz\\.js$", }, }, { @@ -67,7 +66,6 @@ export default { to: { moreThanOneDependencyType: true, dependencyTypesNot: ["type-only"], - pathNot: "node_modules/viz\\.js/viz\\.js$", }, }, { @@ -106,7 +104,7 @@ export default { "or when you want to run on the web - which state-machine-cat actually does).", severity: "error", from: { - pathNot: "^(bin|src/cli|test|tools)", + pathNot: "^(src/cli|test|tools)", }, to: { dependencyTypes: ["core"], @@ -158,25 +156,25 @@ export default { { name: "no-deps-on-cli", comment: - "This module, that's neither in bin/ nor in cli/ nor is a test for either, depends on " + - "cli/ and/ or bin/ code. As bin and cli are there to call other modules and not the other " + + "This module, that's not in cli/ nor is a test for it, depends on " + + "cli/ code. As cli is there to call other modules and not the other " + "way 'round, you're likely putting this module in the wrong spot.", severity: "error", from: { - pathNot: "^src/cli|^bin|^test/cli", + pathNot: "^src/cli|^test/cli", }, to: { - path: "^src/cli|^bin", + path: "^src/cli", }, }, { - name: "no-unreachable-from-bin", + name: "no-unreachable-from-cli", comment: "This module is not reachable from the the command line interface. This means it's likely " + "'dead wood'. Either remove it, or start using it.", severity: "error", from: { - path: "^bin/smcat$", + path: "^src/cli/main.mts$", }, to: { path: "^src/", @@ -221,7 +219,7 @@ export default { to: { path: "^src/", reachable: false, - pathNot: ["\\.d\\.(c|m)?ts$"], + pathNot: ["\\.d\\.(c|m)?ts$", "^src/cli/main.mts$"], }, }, { diff --git a/dist/cli/cli.mjs b/dist/cli/cli.mjs new file mode 100644 index 00000000..6314fb7a --- /dev/null +++ b/dist/cli/cli.mjs @@ -0,0 +1,170 @@ +import { readFileSync } from "node:fs"; +import { parseArgs } from "node:util"; +import satisfies from "semver/functions/satisfies.js"; +import { formatError, displayLicense, transform } from "./actions.mjs"; +import normalize from "./normalize.mjs"; +import validations from "./validations.mjs"; +const $package = JSON.parse( + readFileSync(new URL("../../package.json", import.meta.url), "utf8"), +); +const HELP_TEXT = `Usage: smcat [options] [infile] + +Write beautiful state charts - https://github.com/sverweij/state-machine-cat + +Options: + -T, --output-type ast|dot|eps|json|oldeps|oldps|oldps2|oldsvg|pdf| + png|ps|ps2|scjson|scxml|smcat|svg + (default: "svg") + -I, --input-type smcat|json|scxml (default: "smcat") + -E, --engine dot|circo|fdp|neato|osage|twopi (default: "dot") + -d, --direction top-down|bottom-top|left-right|right-left (default: + "top-down") + -o --output-to File to write to. use - for stdout. + --desugar transform pseudo states into transitions + (!experimental!) + -V, --version output the version number + -l, --license Display license and exit + -h, --help display help for command +`; +function presentError(pError, pErrorStream) { + pErrorStream.write(formatError(pError)); + process.exitCode = 1; +} +function kebabToCamel(pString) { + return pString + .split("-") + .map((pWord, pIndex) => + pIndex === 0 + ? pWord + : pWord.charAt(0).toUpperCase() + pWord.slice(1).toLowerCase(), + ) + .join(""); +} +function camelizeObject(pObject) { + const lNewObject = {}; + for (const lKey in pObject) { + if (Object.hasOwn(pObject, lKey)) { + const camelCaseKey = kebabToCamel(lKey); + lNewObject[camelCaseKey] = pObject[lKey]; + } + } + return lNewObject; +} +function parseArguments(pArguments) { + const lOptions = { + "output-type": { + type: "string", + short: "T", + default: validations.defaultOutputType, + }, + "input-type": { + type: "string", + short: "I", + default: validations.defaultInputType, + }, + engine: { + type: "string", + short: "E", + default: validations.defaultEngine, + }, + direction: { + type: "string", + short: "d", + default: validations.defaultDirection, + }, + "output-to": { + type: "string", + short: "o", + }, + "dot-graph-attrs": { + type: "string", + }, + "dot-node-attrs": { + type: "string", + }, + "dot-edge-attrs": { + type: "string", + }, + desugar: { + type: "boolean", + default: false, + }, + license: { + type: "boolean", + short: "l", + default: false, + }, + help: { + type: "boolean", + short: "h", + default: false, + }, + version: { + type: "boolean", + short: "V", + default: false, + }, + }; + const { values, positionals } = parseArgs({ + args: pArguments, + options: lOptions, + strict: true, + allowPositionals: true, + tokens: false, + }); + values["output-type"] = validations.validOutputType(values["output-type"]); + values["input-type"] = validations.validInputType(values["input-type"]); + values.engine = validations.validEngine(values.engine); + values.direction = validations.validDirection(values.direction); + if (values["dot-graph-attrs"]) + values["dot-graph-attrs"] = validations.validDotAttrs( + values["dot-graph-attrs"], + ); + if (values["dot-node-attrs"]) + values["dot-node-attrs"] = validations.validDotAttrs( + values["dot-node-attrs"], + ); + if (values["dot-edge-attrs"]) + values["dot-edge-attrs"] = validations.validDotAttrs( + values["dot-edge-attrs"], + ); + return { values: camelizeObject(values), positionals }; +} +function assertNodeVersion(pCurrentNodeVersion, pSupportedEngines) { + if (!satisfies(pCurrentNodeVersion, pSupportedEngines)) { + throw new Error( + `\nERROR: your node version (${pCurrentNodeVersion}) is not recent enough.\n` + + ` state-machine-cat is supported on node ${pSupportedEngines}\n\n`, + ); + } +} +export default async function cli(pArguments = process.argv, pOptions) { + const lOptions = { + currentNodeVersion: process.versions.node, + supportedEngines: $package.engines.node, + outStream: process.stdout, + errorStream: process.stderr, + ...pOptions, + }; + try { + assertNodeVersion(lOptions.currentNodeVersion, lOptions.supportedEngines); + const { values, positionals } = parseArguments(pArguments.slice(2)); + if (values.help) { + lOptions.outStream.write(HELP_TEXT, "utf8"); + return; + } + if (values.version) { + lOptions.outStream.write(`${$package.version}\n`, "utf8"); + return; + } + if (values.license) { + displayLicense(lOptions.outStream); + return; + } + await transform( + validations.validateArguments(normalize(positionals[0], values)), + ); + } catch (pError) { + presentError(pError, lOptions.errorStream); + } +} diff --git a/dist/cli/execute-command-line.mjs b/dist/cli/execute-command-line.mjs deleted file mode 100644 index cd3c5c55..00000000 --- a/dist/cli/execute-command-line.mjs +++ /dev/null @@ -1,111 +0,0 @@ -import { readFileSync } from "node:fs"; -import { Command, Option } from "commander"; -import satisfies from "semver/functions/satisfies.js"; -import { formatError, displayLicense, transform } from "./actions.mjs"; -import normalize from "./normalize.mjs"; -import validations from "./validations.mjs"; -const $package = JSON.parse( - readFileSync(new URL("../../package.json", import.meta.url), "utf8"), -); -function presentError(pError, pErrorStream) { - pErrorStream.write(formatError(pError)); - process.exitCode = 1; -} -function parseArguments(pArguments) { - return new Command() - .description( - "Write beautiful state charts - https://github.com/sverweij/state-machine-cat", - ) - .option( - "-T, --output-type ", - validations.validOutputTypeRE, - validations.validOutputType, - validations.defaultOutputType, - ) - .option( - "-I, --input-type ", - validations.validInputTypeRE, - validations.validInputType, - validations.defaultInputType, - ) - .option( - "-E, --engine ", - validations.validEngineRE, - validations.validEngine, - validations.defaultEngine, - ) - .option( - "-d, --direction ", - validations.validDirectionRE, - validations.validDirection, - validations.defaultDirection, - ) - .option("-o --output-to ", "File to write to. use - for stdout.") - .addOption( - new Option( - "--dot-graph-attrs ", - "graph attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs) - .hideHelp(true), - ) - .addOption( - new Option( - "--dot-node-attrs ", - "node attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs) - .hideHelp(true), - ) - .addOption( - new Option( - "--dot-edge-attrs ", - "edge attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs) - .hideHelp(true), - ) - .option( - "--desugar", - "transform pseudo states into transitions (!experimental!)", - ) - .version($package.version) - .option("-l, --license", "Display license and exit") - .arguments("[infile]") - .parse(pArguments); -} -function assertNodeVersion(pCurrentNodeVersion, pSupportedEngines) { - if (!satisfies(pCurrentNodeVersion, pSupportedEngines)) { - throw new Error( - `\nERROR: your node version (${pCurrentNodeVersion}) is not recent enough.\n` + - ` state-machine-cat is supported on node ${pSupportedEngines}\n\n`, - ); - } -} -export default async function executeCommandLine( - pArguments = process.argv, - pOptions, -) { - const lOptions = { - currentNodeVersion: process.versions.node, - supportedEngines: $package.engines.node, - outStream: process.stdout, - errorStream: process.stderr, - ...pOptions, - }; - try { - assertNodeVersion(lOptions.currentNodeVersion, lOptions.supportedEngines); - const lProgram = parseArguments(pArguments); - if (lProgram.opts()?.license) { - displayLicense(lOptions.outStream); - return; - } - await transform( - validations.validateArguments( - normalize(lProgram.args[0], lProgram.opts()), - ), - ); - } catch (pError) { - presentError(pError, lOptions.errorStream); - } -} diff --git a/dist/cli/main.mjs b/dist/cli/main.mjs new file mode 100755 index 00000000..b4acfaca --- /dev/null +++ b/dist/cli/main.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node +import cli from "./cli.mjs"; +await cli(); diff --git a/package-lock.json b/package-lock.json index 8c1893af..a149c444 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@hpcc-js/wasm-graphviz": "1.2.0", "ajv": "8.17.1", - "commander": "12.1.0", "fast-xml-parser": "4.4.1", "handlebars": "4.7.8", "he": "1.2.0", @@ -19,10 +18,8 @@ "traverse": "0.6.8" }, "bin": { - "sm_cat": "bin/smcat.mjs", - "sm-cat": "bin/smcat.mjs", - "smcat": "bin/smcat.mjs", - "state-machine-cat": "bin/smcat.mjs" + "smcat": "dist/cli/main.mjs", + "state-machine-cat": "dist/cli/main.mjs" }, "devDependencies": { "@types/he": "1.2.3", @@ -1845,6 +1842,7 @@ "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, "engines": { "node": ">=18" } diff --git a/package.json b/package.json index fa56519a..4c34bc0d 100644 --- a/package.json +++ b/package.json @@ -24,21 +24,21 @@ "dist/render/scxml/scxml.template.js" ], "scripts": { - "build": "make clean dist pages cli-build && rm -rf dist && tsc && make templates-dist && prettier --cache --log-level warn --write --use-tabs dist", + "build": "make clean dist pages cli-build && rm -rf dist && tsc && make templates-dist && prettier --cache --log-level warn --write --use-tabs dist && chmod 700 dist/cli/main.mjs", "check": "run-s depcruise lint test:cover", - "depcruise": "dependency-cruise bin src test types tools --config config/dependency-cruiser/base.mjs", + "depcruise": "dependency-cruise src test types tools --config config/dependency-cruiser/base.mjs", "depcruise:graph": "run-s depcruise:graph:doc:archi depcruise:graph:doc:deps depcruise:graph:doc:flat-deps", - "depcruise:graph:doc:archi": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type archi | dot -T svg | tee docs/dependency-cruiser-archi-graph.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-archi-graph.html", - "depcruise:graph:doc:deps": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type dot | dot -T svg | tee docs/dependency-cruiser-graph.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-graph.html", - "depcruise:graph:doc:flat-deps": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type flat | dot -T svg | tee docs/dependency-cruiser-graph-flat-dot.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-graph-flat-dot.html", - "depcruise:graph:dev": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type x-dot-webpage --prefix vscode://file/$(pwd)/ --highlight \"$(watskeburt main)\" | browser", - "depcruise:graph:dev:flat": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type flat --prefix vscode://file/$(pwd)/ --highlight \"$(watskeburt main)\"| twopi -Tsvg | depcruise-wrap-stream-in-html | browser", - "depcruise:view-report": "dependency-cruise bin src test types tools --config config/dependency-cruiser/base.mjs --output-type err-html --prefix vscode://file/$(pwd)/ | browser", - "depcruise:github-actions:markdown": "dependency-cruise bin src test types tools --config config/dependency-cruiser/base.mjs --output-type markdown", - "depcruise:github-actions:mermaid": "dependency-cruise bin src --config config/dependency-cruiser/graph.mjs --output-type mermaid", - "depcruise:github-actions:mermaid:affected": "dependency-cruise bin src test types tools --no-cache --config config/dependency-cruiser/base.mjs --output-type mermaid --affected $SHA", - "format": "prettier --cache --log-level warn --write \"bin/*.mjs\" \"{src,test}/**/*.{js,mjs}\" \"{config,test}/**/*.{js,json}\" \"tools/*.{js,mjs,json}\" \"{src,types}/**/*.{ts,mts}\" \"*.{json,yml,md}\" \"docs/{smcat-online-interpreter.js,*.md}\"", - "format:check": "prettier --cache --check \"bin/*.mjs\" \"{src,test}/**/*.{js,mjs}\" \"{config,test}/**/*.{js,json}\" \"tools/*.{js,mjs,json}\" \"{src,types}/**/*.{ts,mts}\" \"*.{json,yml,md}\" \"docs/{smcat-online-interpreter.js,*.md}\"", + "depcruise:graph:doc:archi": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type archi | dot -T svg | tee docs/dependency-cruiser-archi-graph.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-archi-graph.html", + "depcruise:graph:doc:deps": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type dot | dot -T svg | tee docs/dependency-cruiser-graph.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-graph.html", + "depcruise:graph:doc:flat-deps": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type flat | dot -T svg | tee docs/dependency-cruiser-graph-flat-dot.svg | depcruise-wrap-stream-in-html > docs/dependency-cruiser-graph-flat-dot.html", + "depcruise:graph:dev": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type x-dot-webpage --prefix vscode://file/$(pwd)/ --highlight \"$(watskeburt main)\" | browser", + "depcruise:graph:dev:flat": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type flat --prefix vscode://file/$(pwd)/ --highlight \"$(watskeburt main)\"| twopi -Tsvg | depcruise-wrap-stream-in-html | browser", + "depcruise:view-report": "dependency-cruise src test types tools --config config/dependency-cruiser/base.mjs --output-type err-html --prefix vscode://file/$(pwd)/ | browser", + "depcruise:github-actions:markdown": "dependency-cruise src test types tools --config config/dependency-cruiser/base.mjs --output-type markdown", + "depcruise:github-actions:mermaid": "dependency-cruise src --config config/dependency-cruiser/graph.mjs --output-type mermaid", + "depcruise:github-actions:mermaid:affected": "dependency-cruise src test types tools --no-cache --config config/dependency-cruiser/base.mjs --output-type mermaid --affected $SHA", + "format": "prettier --cache --log-level warn --write \"{src,test}/**/*.{js,mjs}\" \"{config,test}/**/*.{js,json}\" \"tools/*.{js,mjs,json}\" \"{src,types}/**/*.{ts,mts}\" \"*.{json,yml,md}\" \"docs/{smcat-online-interpreter.js,*.md}\"", + "format:check": "prettier --cache --check \"{src,test}/**/*.{js,mjs}\" \"{config,test}/**/*.{js,json}\" \"tools/*.{js,mjs,json}\" \"{src,types}/**/*.{ts,mts}\" \"*.{json,yml,md}\" \"docs/{smcat-online-interpreter.js,*.md}\"", "lint": "run-p lint:eslint format:check lint:types", "lint:eslint": "eslint --cache --cache-location node_modules/.cache/eslint/cache.json --color src test config", "lint:types": "run-p lint:types:*", @@ -71,7 +71,6 @@ "version": "run-s build depcruise:graph scm:stage" }, "files": [ - "bin/", "dist/", "types/", "package.json", @@ -91,15 +90,12 @@ "author": "Sander Verweij", "license": "MIT", "bin": { - "smcat": "bin/smcat.mjs", - "sm-cat": "bin/smcat.mjs", - "sm_cat": "bin/smcat.mjs", - "state-machine-cat": "bin/smcat.mjs" + "smcat": "dist/cli/main.mjs", + "state-machine-cat": "dist/cli/main.mjs" }, "dependencies": { "@hpcc-js/wasm-graphviz": "1.2.0", "ajv": "8.17.1", - "commander": "12.1.0", "fast-xml-parser": "4.4.1", "handlebars": "4.7.8", "he": "1.2.0", diff --git a/src/cli/actions.mts b/src/cli/actions.mts index 4fb8ad71..293f0460 100644 --- a/src/cli/actions.mts +++ b/src/cli/actions.mts @@ -1,7 +1,7 @@ import { type Writable } from "node:stream"; import smcat from "../index-node.mjs"; import { getOutStream, getInStream } from "./file-name-to-stream.mjs"; -import type { ICLIRenderOptions } from "./cli.mjs"; +import type { ICLIRenderOptions } from "./cli-types.mjs"; const LICENSE = ` state machine cat - write beautiful state charts diff --git a/src/cli/cli.d.mts b/src/cli/cli-types.d.mts similarity index 100% rename from src/cli/cli.d.mts rename to src/cli/cli-types.d.mts diff --git a/src/cli/cli.mts b/src/cli/cli.mts new file mode 100644 index 00000000..b36669a1 --- /dev/null +++ b/src/cli/cli.mts @@ -0,0 +1,219 @@ +/* eslint-disable max-lines-per-function */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { readFileSync } from "node:fs"; +import { type Writable } from "node:stream"; +import { parseArgs } from "node:util"; +import satisfies from "semver/functions/satisfies.js"; +import { formatError, displayLicense, transform } from "./actions.mjs"; +import normalize from "./normalize.mjs"; +import validations from "./validations.mjs"; + +const $package = JSON.parse( + // eslint-disable-next-line security/detect-non-literal-fs-filename + readFileSync(new URL("../../package.json", import.meta.url), "utf8"), +); + +const HELP_TEXT = `Usage: smcat [options] [infile] + +Write beautiful state charts - https://github.com/sverweij/state-machine-cat + +Options: + -T, --output-type ast|dot|eps|json|oldeps|oldps|oldps2|oldsvg|pdf| + png|ps|ps2|scjson|scxml|smcat|svg + (default: "svg") + -I, --input-type smcat|json|scxml (default: "smcat") + -E, --engine dot|circo|fdp|neato|osage|twopi (default: "dot") + -d, --direction top-down|bottom-top|left-right|right-left (default: + "top-down") + -o --output-to File to write to. use - for stdout. + --desugar transform pseudo states into transitions + (!experimental!) + -V, --version output the version number + -l, --license Display license and exit + -h, --help display help for command +`; + +function presentError(pError: any, pErrorStream: Writable) { + pErrorStream.write(formatError(pError)); + process.exitCode = 1; +} + +function kebabToCamel(pString: string): string { + return pString + .split("-") + .map((pWord: string, pIndex: number) => + pIndex === 0 + ? pWord + : pWord.charAt(0).toUpperCase() + pWord.slice(1).toLowerCase(), + ) + .join(""); +} + +function camelizeObject(pObject: any): Record { + const lNewObject = {}; + + for (const lKey in pObject) { + if (Object.hasOwn(pObject, lKey)) { + const camelCaseKey = kebabToCamel(lKey); + // @ts-expect-error whatever. this just works + // eslint-disable-next-line security/detect-object-injection + lNewObject[camelCaseKey] = pObject[lKey]; + } + } + + return lNewObject; +} + +function parseArguments(pArguments: string[]): { + values: Record; + positionals: string[]; +} { + const lOptions = { + "output-type": { + type: "string", + short: "T", + default: validations.defaultOutputType, + }, + "input-type": { + type: "string", + short: "I", + default: validations.defaultInputType, + }, + engine: { + type: "string", + short: "E", + default: validations.defaultEngine, + }, + direction: { + type: "string", + short: "d", + default: validations.defaultDirection, + }, + "output-to": { + type: "string", + short: "o", + }, + "dot-graph-attrs": { + type: "string", + }, + "dot-node-attrs": { + type: "string", + }, + "dot-edge-attrs": { + type: "string", + }, + desugar: { + type: "boolean", + default: false, + }, + license: { + type: "boolean", + short: "l", + default: false, + }, + help: { + type: "boolean", + short: "h", + default: false, + }, + version: { + type: "boolean", + short: "V", + default: false, + }, + }; + + const { values, positionals } = parseArgs({ + args: pArguments, + // @ts-expect-error whatever + options: lOptions, + strict: true, + allowPositionals: true, + tokens: false, + }); + + // Handle argument validation manually if needed + // @ts-expect-error whatever + values["output-type"] = validations.validOutputType(values["output-type"]); + // @ts-expect-error whatever + values["input-type"] = validations.validInputType(values["input-type"]); + // @ts-expect-error whatever + values.engine = validations.validEngine(values.engine); + // @ts-expect-error whatever + values.direction = validations.validDirection(values.direction); + if (values["dot-graph-attrs"]) + values["dot-graph-attrs"] = validations.validDotAttrs( + // @ts-expect-error whatever + values["dot-graph-attrs"], + ); + if (values["dot-node-attrs"]) + values["dot-node-attrs"] = validations.validDotAttrs( + // @ts-expect-error whatever + values["dot-node-attrs"], + ); + if (values["dot-edge-attrs"]) + values["dot-edge-attrs"] = validations.validDotAttrs( + // @ts-expect-error whatever + values["dot-edge-attrs"], + ); + + return { values: camelizeObject(values), positionals }; +} + +function assertNodeVersion( + pCurrentNodeVersion: string, + pSupportedEngines: string, +) { + /* c8 ignore start */ + if (!satisfies(pCurrentNodeVersion, pSupportedEngines)) { + throw new Error( + `\nERROR: your node version (${pCurrentNodeVersion}) is not recent enough.\n` + + ` state-machine-cat is supported on node ${pSupportedEngines}\n\n`, + ); + } + /* c8 ignore stop */ +} + +interface ICommandLineOptions { + currentNodeVersion: string; + supportedEngines: string; + outStream: Writable; + errorStream: Writable; +} + +export default async function cli( + pArguments = process.argv, + pOptions?: Partial, +) { + const lOptions = { + currentNodeVersion: process.versions.node, + supportedEngines: $package.engines.node, + outStream: process.stdout, + errorStream: process.stderr, + ...pOptions, + }; + try { + assertNodeVersion(lOptions.currentNodeVersion, lOptions.supportedEngines); + + // eslint-disable-next-line no-magic-numbers + const { values, positionals } = parseArguments(pArguments.slice(2)); + + if (values.help) { + (lOptions.outStream as Writable).write(HELP_TEXT, "utf8"); + return; + } + if (values.version) { + (lOptions.outStream as Writable).write(`${$package.version}\n`, "utf8"); + return; + } + if (values.license) { + displayLicense(lOptions.outStream); + return; + } + await transform( + validations.validateArguments(normalize(positionals[0], values)), + ); + } catch (pError) { + presentError(pError, lOptions.errorStream); + } +} diff --git a/src/cli/execute-command-line.mts b/src/cli/execute-command-line.mts deleted file mode 100644 index 121eca92..00000000 --- a/src/cli/execute-command-line.mts +++ /dev/null @@ -1,137 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { readFileSync } from "node:fs"; -import { type Writable } from "node:stream"; -import { Command, Option } from "commander"; -import satisfies from "semver/functions/satisfies.js"; -import { formatError, displayLicense, transform } from "./actions.mjs"; -import normalize from "./normalize.mjs"; -import validations from "./validations.mjs"; - -const $package = JSON.parse( - // eslint-disable-next-line security/detect-non-literal-fs-filename - readFileSync(new URL("../../package.json", import.meta.url), "utf8"), -); - -/** - * @param {any} pError - * @return {void} - */ -function presentError(pError: any, pErrorStream: Writable) { - pErrorStream.write(formatError(pError)); - process.exitCode = 1; -} - -// eslint-disable-next-line max-lines-per-function -function parseArguments(pArguments: string[]): Command { - return new Command() - .description( - "Write beautiful state charts - https://github.com/sverweij/state-machine-cat", - ) - .option( - "-T, --output-type ", - validations.validOutputTypeRE, - validations.validOutputType as any, - validations.defaultOutputType, - ) - .option( - "-I, --input-type ", - validations.validInputTypeRE, - validations.validInputType as any, - validations.defaultInputType, - ) - .option( - "-E, --engine ", - validations.validEngineRE, - validations.validEngine as any, - validations.defaultEngine, - ) - .option( - "-d, --direction ", - validations.validDirectionRE, - validations.validDirection as any, - validations.defaultDirection, - ) - .option("-o --output-to ", "File to write to. use - for stdout.") - .addOption( - new Option( - "--dot-graph-attrs ", - "graph attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs as any) - .hideHelp(true), - ) - .addOption( - new Option( - "--dot-node-attrs ", - "node attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs as any) - .hideHelp(true), - ) - .addOption( - new Option( - "--dot-edge-attrs ", - "edge attributes to pass to the dot render engine", - ) - .argParser(validations.validDotAttrs as any) - .hideHelp(true), - ) - .option( - "--desugar", - "transform pseudo states into transitions (!experimental!)", - ) - .version($package.version) - .option("-l, --license", "Display license and exit") - .arguments("[infile]") - .parse(pArguments); -} - -function assertNodeVersion( - pCurrentNodeVersion: string, - pSupportedEngines: string, -) { - /* c8 ignore start */ - if (!satisfies(pCurrentNodeVersion, pSupportedEngines)) { - throw new Error( - `\nERROR: your node version (${pCurrentNodeVersion}) is not recent enough.\n` + - ` state-machine-cat is supported on node ${pSupportedEngines}\n\n`, - ); - } - /* c8 ignore stop */ -} - -interface IExecuteCommandLineOptions { - currentNodeVersion: string; - supportedEngines: string; - outStream: Writable; - errorStream: Writable; -} - -export default async function executeCommandLine( - pArguments = process.argv, - pOptions?: Partial, -) { - const lOptions: IExecuteCommandLineOptions = { - currentNodeVersion: process.versions.node, - supportedEngines: $package.engines.node, - outStream: process.stdout, - errorStream: process.stderr, - ...pOptions, - }; - try { - assertNodeVersion(lOptions.currentNodeVersion, lOptions.supportedEngines); - const lProgram = parseArguments(pArguments); - - if (lProgram.opts()?.license) { - displayLicense(lOptions.outStream); - return; - } - await transform( - validations.validateArguments( - normalize(lProgram.args[0], lProgram.opts()), - ), - ); - } catch (pError) { - presentError(pError, lOptions.errorStream); - } -} diff --git a/src/cli/main.mts b/src/cli/main.mts new file mode 100755 index 00000000..1f025baf --- /dev/null +++ b/src/cli/main.mts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +// eslint-disable-next-line node/shebang +import cli from "./cli.mjs"; + +await cli(); diff --git a/src/cli/normalize.mts b/src/cli/normalize.mts index d475c8ba..4d7178d2 100644 --- a/src/cli/normalize.mts +++ b/src/cli/normalize.mts @@ -9,7 +9,10 @@ import type { } from "types/state-machine-cat.mjs"; import options from "../options.mjs"; import { parse as parseAttributes } from "./attributes-parser.mjs"; -import type { ICLIRenderOptions, ILooseCLIRenderOptions } from "./cli.mjs"; +import type { + ICLIRenderOptions, + ILooseCLIRenderOptions, +} from "./cli-types.mjs"; type DictionaryType = { [extension: string]: string }; diff --git a/src/cli/validations.mts b/src/cli/validations.mts index d6ac564f..bf0c9304 100644 --- a/src/cli/validations.mts +++ b/src/cli/validations.mts @@ -2,7 +2,7 @@ import fs from "node:fs"; import type { IRenderOptions } from "types/state-machine-cat.mjs"; import smcat from "../index-node.mjs"; import { parse as parseAttributes } from "./attributes-parser.mjs"; -import type { ICLIRenderOptions } from "./cli.mjs"; +import type { ICLIRenderOptions } from "./cli-types.mjs"; const allowedValues = smcat.getAllowedValues(); diff --git a/test/bin.spec.mts b/test/bin.spec.mts index 1f15ae1a..60d80877 100644 --- a/test/bin.spec.mts +++ b/test/bin.spec.mts @@ -4,7 +4,7 @@ import { equal } from "node:assert/strict"; describe("e2e", () => { it("by default renders an svg from an smcat program", () => { const { status, stdout } = spawnSync("node", [ - "bin/smcat.mjs", + "dist/cli/main.mjs", "test/render/fixtures/600-kitchensink.smcat", "-o", "-", diff --git a/test/cli/execute-command-line.spec.mts b/test/cli/cli.spec.mts similarity index 88% rename from test/cli/execute-command-line.spec.mts rename to test/cli/cli.spec.mts index 318646d5..350dcb16 100644 --- a/test/cli/execute-command-line.spec.mts +++ b/test/cli/cli.spec.mts @@ -1,6 +1,6 @@ import { Writable } from "node:stream"; import { match } from "node:assert/strict"; -import executeCLI from "#cli/execute-command-line.mjs"; +import cli from "#cli/cli.mjs"; class WritableTestStream extends Writable { expected: RegExp | RegExp[] = /^$/; @@ -31,7 +31,7 @@ describe("#cli - execute-command-line", () => { ]); const lErrorStream = new WritableTestStream(); - await executeCLI(["node", "smcat.js", "--license"], { + await cli(["node", "smcat.js", "--license"], { outStream: lOutStream, errorStream: lErrorStream, }); @@ -44,7 +44,7 @@ describe("#cli - execute-command-line", () => { /state-machine-cat is supported on node >=20/, ]); - await executeCLI(["node", "smcat.js", "--license"], { + await cli(["node", "smcat.js", "--license"], { outStream: lOutStream, errorStream: lErrorStream, currentNodeVersion: "10.0.0", diff --git a/tools/gendocpics.sh b/tools/gendocpics.sh index dc015669..79686e9d 100644 --- a/tools/gendocpics.sh +++ b/tools/gendocpics.sh @@ -1,39 +1,39 @@ #!/bin/sh set -e DIR=docs/pics -find -X docs/pics/types/*.smcat -exec bin/smcat.mjs -T dot {} ";" +find -X docs/pics/types/*.smcat -exec dist/cli/main.mjs -T dot {} ";" find -X docs/pics/types/*.dot -exec dot -Gdpi=192 -Tpng {} -O ";" rm docs/pics/types/*.dot -bin/smcat.mjs -T dot -o - $DIR/sample.smcat | circo -Gdpi=192 -Tpng -o$DIR/sample.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/00simplest.smcat | dot -Gdpi=192 -Tpng -o$DIR/00simplest.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/01labels.smcat | dot -Gdpi=192 -Tpng -o$DIR/01labels.png -bin/smcat.mjs -T dot -o - $DIR/01labels_better.smcat | dot -Gdpi=192 -Tpng -o$DIR/01labels_better.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/02notes.smcat | dot -Gdpi=192 -Tpng -o$DIR/02notes.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/03initial_and_final.smcat | dot -Gdpi=192 -Tpng -o$DIR/03initial_and_final.png -bin/smcat.mjs -T dot -o - $DIR/03achoice.smcat | circo -Gdpi=192 -Tpng -o$DIR/03achoice.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/03bforkjoin.smcat | dot -Gdpi=192 -Tpng -o$DIR/03bforkjoin.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/03cjunction.smcat | dot -Gdpi=192 -Tpng -o$DIR/03cjunction.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/03dterminate.smcat | dot -Gdpi=192 -Tpng -o$DIR/03dterminate.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/04explicit_state_declarations.smcat | dot -Gdpi=192 -Tpng -o$DIR/04explicit_state_declarations.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/05tape_player.smcat | dot -Gdpi=192 -Tpng -o$DIR/05tape_player.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/07history.smcat | dot -Gdpi=192 -Tpng -o$DIR/07history.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/08parallel.smcat | dot -Gdpi=192 -Tpng -o$DIR/08parallel.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/09labeled_states.smcat | dot -Gdpi=192 -Tpng -o$DIR/09labeled_states.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/10colored_states_and_transitions.smcat | dot -Gdpi=192 -Tpng -o$DIR/10colored_states_and_transitions.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/11active_state.smcat | dot -Gdpi=192 -Tpng -o$DIR/11active_state.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/12state_type_overrides_not_overridden.smcat | dot -Gdpi=192 -Tpng -o$DIR/12state_type_overrides_not_overridden.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/12state_type_overrides.smcat | dot -Gdpi=192 -Tpng -o$DIR/12state_type_overrides.png -bin/smcat.mjs -d left-right -T dot -o - $DIR/on-off.smcat | dot -Gdpi=192 -Tpng -o$DIR/on-off-left-right.png +dist/cli/main.mjs -T dot -o - $DIR/sample.smcat | circo -Gdpi=192 -Tpng -o$DIR/sample.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/00simplest.smcat | dot -Gdpi=192 -Tpng -o$DIR/00simplest.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/01labels.smcat | dot -Gdpi=192 -Tpng -o$DIR/01labels.png +dist/cli/main.mjs -T dot -o - $DIR/01labels_better.smcat | dot -Gdpi=192 -Tpng -o$DIR/01labels_better.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/02notes.smcat | dot -Gdpi=192 -Tpng -o$DIR/02notes.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/03initial_and_final.smcat | dot -Gdpi=192 -Tpng -o$DIR/03initial_and_final.png +dist/cli/main.mjs -T dot -o - $DIR/03achoice.smcat | circo -Gdpi=192 -Tpng -o$DIR/03achoice.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/03bforkjoin.smcat | dot -Gdpi=192 -Tpng -o$DIR/03bforkjoin.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/03cjunction.smcat | dot -Gdpi=192 -Tpng -o$DIR/03cjunction.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/03dterminate.smcat | dot -Gdpi=192 -Tpng -o$DIR/03dterminate.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/04explicit_state_declarations.smcat | dot -Gdpi=192 -Tpng -o$DIR/04explicit_state_declarations.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/05tape_player.smcat | dot -Gdpi=192 -Tpng -o$DIR/05tape_player.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/07history.smcat | dot -Gdpi=192 -Tpng -o$DIR/07history.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/08parallel.smcat | dot -Gdpi=192 -Tpng -o$DIR/08parallel.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/09labeled_states.smcat | dot -Gdpi=192 -Tpng -o$DIR/09labeled_states.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/10colored_states_and_transitions.smcat | dot -Gdpi=192 -Tpng -o$DIR/10colored_states_and_transitions.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/11active_state.smcat | dot -Gdpi=192 -Tpng -o$DIR/11active_state.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/12state_type_overrides_not_overridden.smcat | dot -Gdpi=192 -Tpng -o$DIR/12state_type_overrides_not_overridden.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/12state_type_overrides.smcat | dot -Gdpi=192 -Tpng -o$DIR/12state_type_overrides.png +dist/cli/main.mjs -d left-right -T dot -o - $DIR/on-off.smcat | dot -Gdpi=192 -Tpng -o$DIR/on-off-left-right.png -bin/smcat.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-01-join.smcat | dot -Tpng -o$DIR/desugar-01-join.png -bin/smcat.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-01-join-desugared.smcat | dot -Tpng -o$DIR/desugar-01-join-desugared.png -bin/smcat.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-02-fork.smcat | dot -Tpng -o$DIR/desugar-02-fork.png -bin/smcat.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-02-fork-desugared.smcat | dot -Tpng -o$DIR/desugar-02-fork-desugared.png -bin/smcat.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-03-junction.smcat | dot -Tpng -o$DIR/desugar-03-junction.png -bin/smcat.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-03-junction-desugared.smcat | dot -Tpng -o$DIR/desugar-03-junction-desugared.png -bin/smcat.mjs -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-04-choice.smcat | dot -Tpng -o$DIR/desugar-04-choice.png -bin/smcat.mjs -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-04-choice-desugared.smcat | dot -Tpng -o$DIR/desugar-04-choice-desugared.png -bin/smcat.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-05-initial.smcat | dot -Tpng -o$DIR/desugar-05-initial.png +dist/cli/main.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-01-join.smcat | dot -Tpng -o$DIR/desugar-01-join.png +dist/cli/main.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-01-join-desugared.smcat | dot -Tpng -o$DIR/desugar-01-join-desugared.png +dist/cli/main.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-02-fork.smcat | dot -Tpng -o$DIR/desugar-02-fork.png +dist/cli/main.mjs -d top-down -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-02-fork-desugared.smcat | dot -Tpng -o$DIR/desugar-02-fork-desugared.png +dist/cli/main.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-03-junction.smcat | dot -Tpng -o$DIR/desugar-03-junction.png +dist/cli/main.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-03-junction-desugared.smcat | dot -Tpng -o$DIR/desugar-03-junction-desugared.png +dist/cli/main.mjs -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-04-choice.smcat | dot -Tpng -o$DIR/desugar-04-choice.png +dist/cli/main.mjs -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-04-choice-desugared.smcat | dot -Tpng -o$DIR/desugar-04-choice-desugared.png +dist/cli/main.mjs -d left-right -T dot -o - --dot-graph-attrs "dpi=192" $DIR/desugar-05-initial.smcat | dot -Tpng -o$DIR/desugar-05-initial.png optipng $DIR/*.png optipng $DIR/types/*.png diff --git a/tools/regenerate_render_fixtures.sh b/tools/regenerate_render_fixtures.sh index 859a626c..2f3ec659 100644 --- a/tools/regenerate_render_fixtures.sh +++ b/tools/regenerate_render_fixtures.sh @@ -12,48 +12,48 @@ rm -rf test/render/fixtures/scxml/ mkdir -p test/render/fixtures/scxml echo "2/6 re-generating svg, ps and eps graphviz can deterministally render ..." -find -X test/render/fixtures/*-d-*.smcat -exec bin/smcat.mjs -T oldsvg {} ";" & \ -find -X test/render/fixtures/*-d-*.smcat -exec bin/smcat.mjs -T oldps2 {} ";" & \ -find -X test/render/fixtures/*-d-*.smcat -exec bin/smcat.mjs -T oldeps {} ";" +find -X test/render/fixtures/*-d-*.smcat -exec dist/cli/main.mjs -T oldsvg {} ";" & \ +find -X test/render/fixtures/*-d-*.smcat -exec dist/cli/main.mjs -T oldps2 {} ";" & \ +find -X test/render/fixtures/*-d-*.smcat -exec dist/cli/main.mjs -T oldeps {} ";" echo "3/6 re-generating render json, scjson and scxml fixtures ..." -find -X test/render/fixtures/*.smcat -exec bin/smcat.mjs -T json {} ";" -find -X test/render/fixtures/*.smcat -exec bin/smcat.mjs -T scjson {} ";" -find -X test/render/fixtures/*.smcat -exec bin/smcat.mjs -T scxml {} ";" -find -X test/render/fixtures/*.scxml -exec bin/smcat.mjs -I scxml -T json {} -o {}.re-json ";" +find -X test/render/fixtures/*.smcat -exec dist/cli/main.mjs -T json {} ";" +find -X test/render/fixtures/*.smcat -exec dist/cli/main.mjs -T scjson {} ";" +find -X test/render/fixtures/*.smcat -exec dist/cli/main.mjs -T scxml {} ";" +find -X test/render/fixtures/*.scxml -exec dist/cli/main.mjs -I scxml -T json {} -o {}.re-json ";" echo "4/6 re-generating parse fixtures ..." -find -X test/parse/fixtures/color-*.smcat -exec bin/smcat.mjs -T json {} ";" -find -X test/parse/fixtures/no-color-*.smcat -exec bin/smcat.mjs -T json {} ";" -find -X test/parse/fixtures/no-color-*.smcat -exec bin/smcat.mjs -T dot --dot-node-attrs "color=pink" {} ";" +find -X test/parse/fixtures/color-*.smcat -exec dist/cli/main.mjs -T json {} ";" +find -X test/parse/fixtures/no-color-*.smcat -exec dist/cli/main.mjs -T json {} ";" +find -X test/parse/fixtures/no-color-*.smcat -exec dist/cli/main.mjs -T dot --dot-node-attrs "color=pink" {} ";" echo "5/6 re-generating one-off parse fixtures ..." -bin/smcat.mjs -T json test/parse/fixtures/composite.smcat -bin/smcat.mjs -T dot test/parse/fixtures/composite.smcat -bin/smcat.mjs -T dot --direction bottom-top test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-bottom-top.dot -bin/smcat.mjs -T dot --direction left-right test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-left-right.dot -bin/smcat.mjs -T dot --direction right-left test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-right-left.dot -bin/smcat.mjs -T json test/parse/fixtures/composite_no_root_transitions.smcat -bin/smcat.mjs -T dot test/parse/fixtures/composite_no_root_transitions.smcat -bin/smcat.mjs -T json test/parse/fixtures/kitchensink.smcat -bin/smcat.mjs -T dot test/parse/fixtures/kitchensink.smcat -bin/smcat.mjs -T json test/parse/fixtures/minimal.smcat -bin/smcat.mjs -T dot test/parse/fixtures/minimal.smcat -bin/smcat.mjs -T dot test/parse/fixtures/pseudostates.smcat -bin/smcat.mjs -T json test/parse/fixtures/pseudostates.smcat -bin/smcat.mjs -T dot test/parse/fixtures/parallel-with-non-regular-child.smcat -bin/smcat.mjs -T json test/parse/fixtures/parallel-with-non-regular-child.smcat -bin/smcat.mjs -T dot --direction top-down test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-top-down.dot -bin/smcat.mjs -T dot --direction bottom-top test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-bottom-top.dot -bin/smcat.mjs -T dot --direction left-right test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-left-right.dot -bin/smcat.mjs -T dot --direction right-left test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-right-left.dot -bin/smcat.mjs -T json test/parse/fixtures/compositewithselftransition.smcat -bin/smcat.mjs -T dot --direction top-down test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-top-down.dot -bin/smcat.mjs -T dot --direction bottom-top test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-bottom-top.dot -bin/smcat.mjs -T dot --direction left-right test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-left-right.dot -bin/smcat.mjs -T dot --direction right-left test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-right-left.dot -bin/smcat.mjs -T dot test/parse/fixtures/states-with-a-label.smcat -bin/smcat.mjs -T json test/parse/fixtures/states-with-a-label.smcat +dist/cli/main.mjs -T json test/parse/fixtures/composite.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/composite.smcat +dist/cli/main.mjs -T dot --direction bottom-top test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-bottom-top.dot +dist/cli/main.mjs -T dot --direction left-right test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-left-right.dot +dist/cli/main.mjs -T dot --direction right-left test/parse/fixtures/composite.smcat -o test/parse/fixtures/composite-right-left.dot +dist/cli/main.mjs -T json test/parse/fixtures/composite_no_root_transitions.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/composite_no_root_transitions.smcat +dist/cli/main.mjs -T json test/parse/fixtures/kitchensink.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/kitchensink.smcat +dist/cli/main.mjs -T json test/parse/fixtures/minimal.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/minimal.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/pseudostates.smcat +dist/cli/main.mjs -T json test/parse/fixtures/pseudostates.smcat +dist/cli/main.mjs -T dot test/parse/fixtures/parallel-with-non-regular-child.smcat +dist/cli/main.mjs -T json test/parse/fixtures/parallel-with-non-regular-child.smcat +dist/cli/main.mjs -T dot --direction top-down test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-top-down.dot +dist/cli/main.mjs -T dot --direction bottom-top test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-bottom-top.dot +dist/cli/main.mjs -T dot --direction left-right test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-left-right.dot +dist/cli/main.mjs -T dot --direction right-left test/parse/fixtures/pseudostates.smcat -o test/parse/fixtures/pseudostates-right-left.dot +dist/cli/main.mjs -T json test/parse/fixtures/compositewithselftransition.smcat +dist/cli/main.mjs -T dot --direction top-down test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-top-down.dot +dist/cli/main.mjs -T dot --direction bottom-top test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-bottom-top.dot +dist/cli/main.mjs -T dot --direction left-right test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-left-right.dot +dist/cli/main.mjs -T dot --direction right-left test/parse/fixtures/compositewithselftransition.smcat -o test/parse/fixtures/compositewithselftransition-right-left.dot +dist/cli/main.mjs -T dot test/parse/fixtures/states-with-a-label.smcat +dist/cli/main.mjs -T json test/parse/fixtures/states-with-a-label.smcat echo "6/6 formatting results ..."