From 90aa314633d6819a28f5fe188d52823ac723e6ca Mon Sep 17 00:00:00 2001 From: BluJ Date: Fri, 7 Apr 2023 13:51:10 -0600 Subject: [PATCH] chore: Add better sh --- lib/test/util.shell.test.ts | 39 +++++++++++++++++++++++++++ lib/util/shell.ts | 53 +++++++++++++++++++++++++++++++++++-- package-lock.json | 4 +-- package.json | 2 +- 4 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 lib/test/util.shell.test.ts diff --git a/lib/test/util.shell.test.ts b/lib/test/util.shell.test.ts new file mode 100644 index 0000000..4cc49d0 --- /dev/null +++ b/lib/test/util.shell.test.ts @@ -0,0 +1,39 @@ +import { sh } from "../util"; + +describe("Util shell values bluj ", () => { + test("simple", () => { + expect(sh("echo hello")).toEqual({ command: "echo", args: ["hello"] }); + }, 1); + test("simple 2", () => { + expect(sh("echo hello world")).toEqual({ + command: "echo", + args: ["hello", "world"], + }); + }, 1); + test("simple A double quote", () => { + expect(sh('echo "hello world" ')).toEqual({ + command: "echo", + args: ["hello world"], + }); + }, 1); + test("simple A sing quote", () => { + expect(sh("echo 'hello world' ")).toEqual({ + command: "echo", + args: ["hello world"], + }); + }, 1); + test("simple complex", () => { + expect(sh("echo arg1 'arg2 and' arg3 \"arg4 \" ")).toEqual({ + command: "echo", + args: ["arg1", "arg2 and", "arg3", "arg4 "], + }); + }, 1); + test("nested", () => { + expect( + sh(`echo " 'arg1 ' " ' " arg2" ' arg4'"`) + ).toEqual({ + command: "echo", + args: [` 'arg1 ' `, ` " arg2" `, `arg4'"`], + }); + }, 1); +}); diff --git a/lib/util/shell.ts b/lib/util/shell.ts index 8d0fbb3..68522bf 100644 --- a/lib/util/shell.ts +++ b/lib/util/shell.ts @@ -1,8 +1,57 @@ import { Effects } from "../types"; +const match = + (regex: RegExp) => + (s: string): [string, string, string] => { + const matched = s.match(regex); + if (matched === null) { + return [s, "", ""]; + } + const [ + _all, + _firstSet, + notQuote, + _maybeQuote, + doubleQuoted, + singleQuoted, + rest, + noQuotes, + ] = matched; + const quoted = doubleQuoted || singleQuoted; + if (!!noQuotes) { + return [noQuotes || "", "", ""]; + } + if (!notQuote && !quoted) { + return [rest || "", "", ""]; + } + return [notQuote || "", quoted || "", rest || ""]; + }; +const quotes = match(/^((.*?)("([^\"]*)"|'([^\']*)')(.*)|(.*))$/); +const quoteSeperated = (s: string, quote: typeof quotes) => { + const values = []; + + let value = quote(s); + while (true) { + if (value[0].length) { + values.push(...value[0].split(" ")); + } + if (value[1].length) { + values.push(value[1]); + } + if (!value[2].length) { + break; + } + value = quote(value[2]); + } + return values; +}; + export function sh(shellCommand: string) { + const [command, ...args] = quoteSeperated(shellCommand, quotes).filter( + Boolean + ); return { - command: "sh", - args: ["-c", shellCommand], + command, + args, } satisfies Parameters[0]; } diff --git a/package-lock.json b/package-lock.json index e7e4ebf..d90d4a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie7", + "version": "0.4.0-lib0.charlie8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "start-sdk", - "version": "0.4.0-lib0.charlie7", + "version": "0.4.0-lib0.charlie8", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index b10b7ed..37fab68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "start-sdk", - "version": "0.4.0-lib0.charlie7", + "version": "0.4.0-lib0.charlie8", "description": "For making the patterns that are wanted in making services for the startOS.", "main": "./lib/index.js", "types": "./lib/index.d.ts",