UNPKG

jssm

Version:

A Javascript finite state machine (FSM) with a terse DSL and a simple API. Most FSMs are one-liners. Fast, easy, powerful, well tested, typed with TypeScript, and visualizations. MIT License.

8 lines 4.52 kB
#!/usr/bin/env node "use strict";var fs=require("fs");var path=require("path");var child_process=require("child_process");var _a;const IS_WINDOWS=process.platform==="win32";const PATH_SEP=IS_WINDOWS?";":":";const PATHEXT=IS_WINDOWS?((_a=process.env.PATHEXT)!==null&&_a!==void 0?_a:".COM;.EXE;.BAT;.CMD").split(";").map(s=>s.toLowerCase()):[""];async function findPluginOnPath(subcommand,pathEnv=process.env.PATH){if(!pathEnv)return null;const dirs=pathEnv.split(PATH_SEP).filter(d=>d.length>0);const baseName=`fsl-${subcommand}`;const NODE_EXTS=[".cjs",".mjs",".js"];const exts=IS_WINDOWS?[...PATHEXT,...NODE_EXTS]:["",...NODE_EXTS];for(const dir of dirs){for(const ext of exts){const candidate=path.join(dir,baseName+ext);try{const st=await fs.promises.stat(candidate);if(st.isFile())return candidate}catch(_a){}}}return null}function isInProcessEligible(resolvedPath){const ext=path.extname(resolvedPath).toLowerCase();if(ext!==".js"&&ext!==".mjs"&&ext!==".cjs")return false;const norm=resolvedPath.replace(/\\/g,"/");return norm.includes("/node_modules/")}async function invokeInProcess(pluginPath,argv){var _a,_b;const originalExit=process.exit;const originalArgv=process.argv;let interceptedExit=null;const ExitInterception=Symbol("ExitInterception");process.exit=code=>{interceptedExit=typeof code==="number"?code:0;throw ExitInterception};process.argv=[originalArgv[0],pluginPath,...argv];try{const mod=await import(pluginPath);const cli=mod&&((_a=mod.default)!==null&&_a!==void 0?_a:mod);if(typeof cli!=="function"){process.stderr.write(`fsl: error: plugin ${pluginPath} is missing default cli() export\n`);return 2}const result=await cli(argv);return typeof result==="number"?result:0}catch(e){if(e===ExitInterception&&interceptedExit!==null){return interceptedExit}process.stderr.write(`fsl: error: plugin threw: ${(_b=e.message)!==null&&_b!==void 0?_b:String(e)}\n`);return 2}finally{process.exit=originalExit;process.argv=originalArgv}}async function invokeBySpawn(pluginPath,argv){return new Promise(res=>{const ext=path.extname(pluginPath).toLowerCase();const isCmdScript=IS_WINDOWS&&(ext===".cmd"||ext===".bat");const isNodeScript=ext===".cjs"||ext===".mjs"||ext===".js";const[spawnCmd,spawnArgs]=isCmdScript?["cmd.exe",["/c",pluginPath,...argv]]:isNodeScript?[process.execPath,[pluginPath,...argv]]:[pluginPath,argv];const child=child_process.spawn(spawnCmd,spawnArgs,{stdio:"inherit"});child.on("exit",(code,signal)=>{if(signal)res(128+(process.platform==="win32"?1:0));else res(typeof code==="number"?code:2)});child.on("error",err=>{process.stderr.write(`fsl: error: failed to spawn plugin: ${err.message}\n`);res(2)})})}const RESERVED_FLAGS=new Set(["--help","-h","--version","-V"]);const RESERVED_NAMES=new Set(["help","version"]);const getDispatcherVersion=()=>"5.120.0";const printDispatcherHelp=()=>{process.stdout.write(`fsl — finite-state language toolchain dispatcher\n\nUsage:\n fsl <subcommand> [options] [args...]\n fsl [--help|--version]\n\nBuilt-in subcommands (resolved via PATH):\n render Render FSL machines to SVG, DOT, PNG, JPEG, or HTML\n\nDiscovery:\n Any \`fsl-<name>\` executable on PATH is dispatched when you run\n \`fsl <name>\`. Third-party plugins follow the same contract as\n first-party ones.\n\n See: https://github.com/StoneCypher/jssm\n`)};async function dispatch(argv){let verbose=false;if(argv[0]==="--verbose"){verbose=true;argv=argv.slice(1)}if(argv.length===0||RESERVED_FLAGS.has(argv[0])){if(argv[0]==="--version"||argv[0]==="-V"){process.stdout.write(`fsl ${getDispatcherVersion()}\n`);return 0}printDispatcherHelp();return 0}const subcommand=argv[0];const rest=argv.slice(1);if(RESERVED_NAMES.has(subcommand)){if(subcommand==="version"){process.stdout.write(`fsl ${getDispatcherVersion()}\n`);return 0}printDispatcherHelp();return 0}const pluginPath=await findPluginOnPath(subcommand);if(!pluginPath){process.stderr.write(`fsl: '${subcommand}' is not a known subcommand and no \`fsl-${subcommand}\` was found on PATH\n`);return 1}if(verbose){process.stderr.write(`fsl: resolved '${subcommand}' to ${pluginPath}\n`)}if(isInProcessEligible(pluginPath)){return invokeInProcess(pluginPath,rest)}return invokeBySpawn(pluginPath,rest)} /** * Binary entry for `fsl` (and its alias `jssm`). Forwards argv to * `dispatch()` and exits with the returned code. * * The shebang (`#!/usr/bin/env node`) is added by rollup at build time. */async function main(){const argv=process.argv.slice(2);const code=await dispatch(argv);process.exit(code)}void main();