From 2b226ec1939ddaa0fd297a8b9ebfa0276e8aa504 Mon Sep 17 00:00:00 2001 From: Grant Gurvis Date: Mon, 6 Nov 2023 09:34:21 -0800 Subject: [PATCH 1/3] Update types --- .vscode/settings.json | 2 +- migrate.ts | 30 +++++ package.json | 4 +- src/@usermn/sdc/index.ts | 7 +- src/amplify.ts | 2 +- src/ansible-doc.ts | 2 +- src/apt.ts | 4 +- src/asdf.ts | 39 ++++--- src/assimp.ts | 4 +- src/aws-vault.ts | 2 +- src/aws.ts | 11 +- src/aws/acm.ts | 12 +- src/aws/amplify.ts | 42 +++---- src/aws/cloudformation.ts | 83 +++++++------ src/aws/cloudwatch.ts | 28 ++--- src/aws/ec2.ts | 226 ++++++++++++++++++++++++++++-------- src/aws/ecs.ts | 72 +++++++----- src/aws/eks.ts | 24 ++-- src/aws/elasticbeanstalk.ts | 54 +++++---- src/aws/iam.ts | 46 +++++--- src/aws/lambda.ts | 167 +++++++++++++++----------- src/aws/s3.ts | 4 +- src/aws/s3api.ts | 2 +- src/aws/secretsmanager.ts | 29 +++-- src/bat.ts | 6 +- src/bazel.ts | 6 +- src/black.ts | 2 +- src/bosh.ts | 2 +- src/brew.ts | 10 +- src/bun.ts | 17 ++- src/bundle.ts | 2 +- src/capacitor.ts | 9 +- src/cargo.ts | 67 ++++++----- src/cf.ts | 4 +- src/checkov.ts | 2 +- src/chsh.ts | 2 +- src/composer.ts | 31 +++-- src/conda.ts | 8 +- src/copilot.ts | 2 +- src/cordova.ts | 4 +- src/dcli.ts | 4 +- src/defaultbrowser.ts | 2 +- src/defaults.ts | 2 +- src/degit.ts | 9 +- src/deno/generators.ts | 2 +- src/deta.ts | 24 ++-- src/docker.ts | 57 +++++---- src/doppler.ts | 8 +- src/dotnet.ts | 7 +- src/dotnet/dotnet-add.ts | 16 ++- src/dotnet/dotnet-new.ts | 10 +- src/dotnet/dotnet-run.ts | 2 +- src/dotnet/dotnet-tool.ts | 16 ++- src/dscacheutil.ts | 7 +- src/dtm.ts | 2 +- src/eb.ts | 2 +- src/elm-format.ts | 21 ++-- src/elm-json.ts | 9 +- src/elm-review.ts | 25 ++-- src/elm.ts | 9 +- src/env.ts | 15 +-- src/envchain.ts | 2 +- src/expo-cli.ts | 14 ++- src/expo.ts | 18 ++- src/expressots.ts | 4 +- src/ffmpeg.ts | 18 +-- src/fig-teams.ts | 8 -- src/fig-teams@latest.ts | 119 ------------------- src/fig/index.ts | 8 +- src/fig/shared.ts | 107 ++++++++++++----- src/fin.ts | 8 +- src/firebase.ts | 2 +- src/fisher.ts | 2 +- src/flutter.ts | 4 +- src/flyctl.ts | 4 +- src/fnm.ts | 4 +- src/fvm.ts | 2 +- src/gem.ts | 20 ++-- src/gh.ts | 52 ++++++--- src/gibo.ts | 2 +- src/git-cliff.ts | 2 +- src/git-flow.ts | 26 +++-- src/git-profile.ts | 2 +- src/git.ts | 113 +++++++++++++----- src/go.ts | 2 +- src/gource.ts | 2 +- src/gpg.ts | 4 +- src/heroku.ts | 2 +- src/hugo.ts | 2 +- src/hyper.ts | 2 +- src/ibus.ts | 2 +- src/ignite-cli.ts | 2 +- src/infracost/index.ts | 8 +- src/ipatool.ts | 12 +- src/j.ts | 14 ++- src/jenv.ts | 8 +- src/just.ts | 14 ++- src/k3d.ts | 8 +- src/k9s.ts | 2 +- src/kill.ts | 2 +- src/kind.ts | 4 +- src/kool.ts | 4 +- src/kubectl.ts | 39 +++++-- src/kubectx.ts | 4 +- src/kubens.ts | 2 +- src/launchctl.ts | 2 +- src/lerna.ts | 16 +-- src/limactl.ts | 2 +- src/login.ts | 2 +- src/lsof.ts | 8 +- src/m.ts | 8 +- src/mackup.ts | 2 +- src/mamba.ts | 6 +- src/man.ts | 7 +- src/mdfind.ts | 33 ++++-- src/meteor.ts | 6 +- src/mix.ts | 4 +- src/mkinitcpio.ts | 2 +- src/mount.ts | 4 +- src/multipass.ts | 10 +- src/n.ts | 2 +- src/networkQuality.ts | 2 +- src/ng.ts | 2 +- src/npx.ts | 6 +- src/ns.ts | 11 +- src/nx.ts | 2 +- src/okteto.ts | 4 +- src/op.ts | 2 +- src/open.ts | 13 ++- src/pandoc.ts | 72 +++++++----- src/pass.ts | 31 +++-- src/php.ts | 41 +++++-- src/php/artisan.ts | 9 +- src/php/bin-console.ts | 7 +- src/php/please.ts | 7 +- src/phpunit-watcher.ts | 2 +- src/pip.ts | 2 +- src/pipx.ts | 2 +- src/pkgutil.ts | 18 ++- src/pnpm.ts | 4 +- src/pre-commit.ts | 2 +- src/projj.ts | 20 +++- src/pulumi.ts | 2 +- src/pyenv.ts | 4 +- src/quickmail.ts | 2 +- src/r.ts | 2 +- src/rails.ts | 2 +- src/rake.ts | 2 +- src/rancher.ts | 2 +- src/rbenv.ts | 4 +- src/rclone.ts | 2 +- src/react-native.ts | 8 +- src/redwood.ts | 5 +- src/rugby.ts | 2 +- src/rush.ts | 5 +- src/rustup.ts | 15 ++- src/scarb.ts | 36 +++++- src/scc.ts | 32 +++-- src/serverless.ts | 10 +- src/shadcn-ui.ts | 12 +- src/shopify/index.ts | 7 +- src/shortcuts.ts | 4 +- src/snaplet.ts | 6 +- src/softwareupdate.ts | 2 +- src/spring.ts | 15 ++- src/ssh.ts | 41 +++++-- src/stepzen.ts | 4 +- src/svtplay-dl.ts | 2 +- src/sysctl.ts | 2 +- src/systemctl.ts | 32 +++-- src/tailscale.ts | 2 +- src/task.ts | 8 +- src/task/go-task.ts | 2 +- src/task/taskwarrior.ts | 16 +-- src/terraform.ts | 4 +- src/terragrunt.ts | 4 +- src/tfenv.ts | 4 +- src/tfsec.ts | 2 +- src/tmuxinator.ts | 4 +- src/tokei.ts | 2 +- src/trap.ts | 2 +- src/trex.ts | 4 +- src/tsh.ts | 6 +- src/tsuru.ts | 10 +- src/turbo.ts | 5 +- src/unset.ts | 2 +- src/v.ts | 2 +- src/valet.ts | 2 +- src/vercel.ts | 24 +--- src/vr.ts | 9 +- src/vsce.ts | 2 +- src/vultr-cli.ts | 2 +- src/watson.ts | 6 +- src/wd.ts | 11 +- src/xc.ts | 8 +- src/xcodes.ts | 4 +- src/yalc.ts | 2 +- src/yarn.ts | 25 +++- src/ykman.ts | 2 +- src/yo.ts | 2 +- src/youtube-dl.ts | 12 +- src/z.ts | 37 +++--- src/zellij.ts | 2 +- yarn.lock | 20 ++-- 204 files changed, 1771 insertions(+), 1112 deletions(-) create mode 100644 migrate.ts delete mode 100644 src/fig-teams.ts delete mode 100644 src/fig-teams@latest.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a0a4b2ce529..b24c139437d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,6 @@ "source.fixAll.eslint": true }, "editor.formatOnSave": true, - "deno.enablePaths": [".cicada"], + "deno.enablePaths": ["migrate.ts"], "deno.enable": true } diff --git a/migrate.ts b/migrate.ts new file mode 100644 index 000000000000..0ca3bfc51e81 --- /dev/null +++ b/migrate.ts @@ -0,0 +1,30 @@ +import { walkSync } from "https://deno.land/std/fs/walk.ts"; +import { split } from "npm:shlex"; + +const walk = walkSync("src"); + +for (const entry of walk) { + // console.log(entry.path); + + if (entry.isFile) { + const file = Deno.readTextFileSync(`${entry.path}`); + + const re = /script:\s"([^"]*)"/g; + const newFile = file.replaceAll(re, (match) => { + const a = re.exec(match); + if (a && a[1]) { + console.log(a[1]); + console.log(split(a[1])); + if (confirm("abc")) { + return `script: ${JSON.stringify(split(a[1]))}`; + } else { + return match; + } + } else { + return match; + } + }); + + Deno.writeTextFileSync(entry.path, newFile); + } +} diff --git a/package.json b/package.json index cc74b868de28..97275cf2db15 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@typescript-eslint/parser": "^6.7.3", "@vitejs/plugin-react": "^4.1.0", "@withfig/autocomplete-tools": "^2.7.10", - "@withfig/autocomplete-types": "^1.28.0", + "@withfig/autocomplete-types": "^1.29.0", "autoprefixer": "^10.4.16", "chalk": "^5.3.0", "chokidar": "^3.5.3", @@ -96,7 +96,7 @@ "vite-plugin-externals": "^0.6.2" }, "dependencies": { - "@fig/autocomplete-generators": "^2.2.5", + "@fig/autocomplete-generators": "^2.3.0", "@fig/autocomplete-helpers": "^1.0.7", "@fig/autocomplete-hooks": "^1.0.2", "@withfig/api-bindings": "^0.30.3", diff --git a/src/@usermn/sdc/index.ts b/src/@usermn/sdc/index.ts index 79078c2bd6d6..406588fe6f63 100644 --- a/src/@usermn/sdc/index.ts +++ b/src/@usermn/sdc/index.ts @@ -3,6 +3,11 @@ const versionFiles = ["0.0.0"]; export const getVersionCommand: Fig.GetVersionCommand = async ( executeShellCommand ) => { - return await executeShellCommand("npx @usermn/sdc --version"); + return ( + await executeShellCommand({ + command: "npx", + args: ["@usermn/sdc", "--version"], + }) + ).stdout; }; export default createVersionedSpec("@usermn/sdc", versionFiles); diff --git a/src/amplify.ts b/src/amplify.ts index e9e7a055b1c9..3fa44095a97b 100644 --- a/src/amplify.ts +++ b/src/amplify.ts @@ -1,5 +1,5 @@ const envNameGenerator: Fig.Generator = { - script: "amplify env list --json", + script: ["amplify", "env", "list", "--json"], postProcess: function (out) { const envContent = JSON.parse(out); return envContent["envs"].map((env: string) => { diff --git a/src/ansible-doc.ts b/src/ansible-doc.ts index 063f0beaf083..1dc72912eb97 100644 --- a/src/ansible-doc.ts +++ b/src/ansible-doc.ts @@ -1,5 +1,5 @@ const allPluginsGenerator: Fig.Generator = { - script: "ansible-doc --list --json 2>/dev/null", + script: ["ansible-doc", "--list", "--json"], postProcess: function (output) { const plugins = JSON.parse(output); return Object.keys(plugins).map((key) => ({ diff --git a/src/apt.ts b/src/apt.ts index 3ef740dbf989..d297075f3ebc 100644 --- a/src/apt.ts +++ b/src/apt.ts @@ -30,7 +30,7 @@ const packages: Fig.Generator = { }; const installedPackages: Fig.Generator = { - script: "apt list --installed", + script: ["apt", "list", "--installed"], postProcess: function (a) { return a .trim() @@ -46,7 +46,7 @@ const installedPackages: Fig.Generator = { }; const upgradablePackages: Fig.Generator = { - script: "apt list --upgradable", + script: ["apt", "list", "--upgradable"], postProcess: function (a) { return a .trim() diff --git a/src/asdf.ts b/src/asdf.ts index 53eaeb2647ee..2de3174d5c1d 100644 --- a/src/asdf.ts +++ b/src/asdf.ts @@ -1,6 +1,4 @@ const PRIORITY_TOP_THRESHOLD = 76; -const LS_BIN = "/bin/ls"; -const ASDF_DATA_DIR = "~/.asdf"; const HOUR_IN_MILLISECONDS = 3600000; /* @@ -9,7 +7,7 @@ const HOUR_IN_MILLISECONDS = 3600000; const installedPluginNamesGenerator = ( suggestOptions?: Partial ): Fig.Generator => ({ - script: "asdf plugin-list", + script: ["asdf", "plugin-list"], postProcess: (output) => output.split("\n").map((pluginName) => ({ name: `${pluginName}`, @@ -24,15 +22,23 @@ const allPluginNamesGenerator = ( suggestOptions?: Partial ): Fig.Generator => ({ // If use `asdf plugin-list-all`, it will time out, so use `ls`. - script: `${LS_BIN} -1 ${ASDF_DATA_DIR}/repository/plugins`, - postProcess: (output) => - output.split("\n").map((pluginName) => ({ + custom: async (_, executeCommand, generatorContext) => { + const { stdout } = await executeCommand({ + command: "ls", + args: [ + "-1", + `${generatorContext.environmentVariables["HOME"]}/.asdf/repository/plugins`, + ], + }); + + return stdout.split("\n").map((pluginName) => ({ name: `${pluginName}`, description: "Plugin name", priority: PRIORITY_TOP_THRESHOLD, icon: "fig://icon?type=package", ...suggestOptions, - })), + })); + }, }); const installedPluginVersionsGenerator = ( @@ -41,7 +47,7 @@ const installedPluginVersionsGenerator = ( ): Fig.Generator => ({ script: (context) => { const pluginName = context[context.length - 2]; - return `asdf list ${pluginName}`; + return ["asdf", "list", pluginName]; }, postProcess: (output) => output @@ -63,7 +69,7 @@ const allPluginVersionsGenerator = ( ): Fig.Generator => ({ script: (context) => { const pluginName = context[context.length - 2]; - return `asdf list-all ${pluginName}`; + return ["asdf", "list-all", pluginName]; }, cache: { ttl: HOUR_IN_MILLISECONDS, @@ -86,15 +92,22 @@ const shimNamesGenerator = ( suggestOptions?: Partial ): Fig.Generator => ({ // Use `ls` because there is no command to get shims in `asdf`. - script: `${LS_BIN} -1 ${ASDF_DATA_DIR}/shims`, - postProcess: (output) => - output.split("\n").map((shimName) => ({ + custom: async (_, executeCommand, generatorContext) => { + const { stdout } = await executeCommand({ + command: "ls", + args: [ + "-1", + `${generatorContext.environmentVariables["HOME"]}/.asdf/shims`, + ], + }); + return stdout.split("\n").map((shimName) => ({ name: `${shimName}`, description: "Shim name", priority: PRIORITY_TOP_THRESHOLD, icon: "fig://icon?type=command", ...suggestOptions, - })), + })); + }, }); /* diff --git a/src/assimp.ts b/src/assimp.ts index 52698f690486..163a31ed8033 100644 --- a/src/assimp.ts +++ b/src/assimp.ts @@ -21,7 +21,7 @@ const commonOptions: Fig.Option[] = [ ]; const importExtGenerator: Fig.Generator = { - script: "assimp listext", + script: ["assimp", "listext"], postProcess: function (out) { return out.split(";").map((ext) => { return { @@ -33,7 +33,7 @@ const importExtGenerator: Fig.Generator = { }; const exportExtGenerator: Fig.Generator = { - script: "assimp listexport", + script: ["assimp", "listexport"], postProcess: function (out) { return out.split("\n").map((ext) => { return { diff --git a/src/aws-vault.ts b/src/aws-vault.ts index c6b76a35ba5e..dea41f6563c4 100644 --- a/src/aws-vault.ts +++ b/src/aws-vault.ts @@ -1,5 +1,5 @@ const profilesGenerator: Fig.Generator = { - script: "aws-vault list --profiles", + script: ["aws-vault", "list", "--profiles"], postProcess(out) { return out.split("\n").map((name) => ({ name })); }, diff --git a/src/aws.ts b/src/aws.ts index 97dee14c8924..dcf46eed5a7c 100644 --- a/src/aws.ts +++ b/src/aws.ts @@ -3,7 +3,7 @@ export const awsProfileGenerator: Fig.Generator = { strategy: "stale-while-revalidate", cacheByDirectory: true, }, - script: "aws configure list-profiles", + script: ["aws", "configure", "list-profiles"], postProcess: function (out) { if (out.trim() == "") { return []; @@ -19,10 +19,11 @@ export const awsProfileGenerator: Fig.Generator = { const completionSpec: Fig.Spec = { name: "aws", async generateSpec(_, executeShellCommand) { - const check = await executeShellCommand( - "ls ~/.aws/credentials && ls ~/.aws/config" - ); - const prioritize = check.includes("No such file or directory"); + const { stdout } = await executeShellCommand({ + command: "bash", + args: ["-c", "ls ~/.aws/credentials && ls ~/.aws/config"], + }); + const prioritize = stdout.includes("No such file or directory"); return { name: "aws", subcommands: [ diff --git a/src/aws/acm.ts b/src/aws/acm.ts index 4963d8afbd18..dd4e8af97a82 100644 --- a/src/aws/acm.ts +++ b/src/aws/acm.ts @@ -31,12 +31,12 @@ const postPrecessGenerator = ( const _prefixFile = "file://"; const _prefixBlob = "fileb://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -51,7 +51,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -157,7 +157,7 @@ const generators: Record = { }, listCertificates: { - script: "aws acm list-certificates", + script: ["aws", "acm", "list-certificates"], postProcess: (out) => { return postPrecessGenerator( out, @@ -168,7 +168,7 @@ const generators: Record = { }, listCertificateAuthorities: { - script: "aws acm-pca list-certificate-authorities", + script: ["aws", "acm-pca", "list-certificate-authorities"], postProcess: (out) => { return postPrecessGenerator(out, "CertificateAuthorities", "Arn"); }, diff --git a/src/aws/amplify.ts b/src/aws/amplify.ts index ee64fbd3f6a4..3e2652c2641b 100644 --- a/src/aws/amplify.ts +++ b/src/aws/amplify.ts @@ -38,28 +38,30 @@ const postPrecessGenerator = ( const customGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws amplify ${command}`; + let args = ["amplify", command]; - for (let i = 0; i < options.length; i++) { - const option = options[i]; + for (const option of options) { const idx = tokens.indexOf(option); if (idx < 0) { continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -70,8 +72,8 @@ const customGenerator = async ( ]; } - return list.map((elm) => { - const name = (childKey ? elm[childKey] : elm) as string; + return list.map((resource) => { + const name = (childKey ? resource[childKey] : resource) as string; return { name, icon: "fig://icon?type=aws", @@ -85,12 +87,12 @@ const customGenerator = async ( const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -105,7 +107,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -194,7 +196,7 @@ const generators: Record = { }, listAmplifyServiceRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => { try { const list = JSON.parse(out)["Roles"]; @@ -223,21 +225,21 @@ const generators: Record = { }, listAmplifyAppIds: { - script: "aws amplify list-apps", + script: ["aws", "amplify", "list-apps"], postProcess: (out) => { return postPrecessGenerator(out, "apps", "appId"); }, }, listAmplifyAppArns: { - script: "aws amplify list-apps", + script: ["aws", "amplify", "list-apps"], postProcess: (out) => { return postPrecessGenerator(out, "apps", "appArn"); }, }, listCfnStackNames: { - script: "aws cloudformation list-stacks", + script: ["aws", "cloudformation", "list-stacks"], postProcess: (out) => { return postPrecessGenerator(out, "StackSummaries", "StackName"); }, @@ -309,7 +311,7 @@ const generators: Record = { }, listIamRoleArns: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => { return postPrecessGenerator(out, "Roles", "Arn"); }, @@ -342,14 +344,14 @@ const generators: Record = { }, listWebhookIds: { - script: "aws amplify list-webhooks", + script: ["aws", "amplify", "list-webhooks"], postProcess: (out) => { return postPrecessGenerator(out, "webhooks", "webhookId"); }, }, listAllBranches: { - script: "aws amplify list-apps", + script: ["aws", "amplify", "list-apps"], postProcess: (out) => { try { const list = JSON.parse(out)["Roles"]; diff --git a/src/aws/cloudformation.ts b/src/aws/cloudformation.ts index 42c64a8edba4..708771e51e08 100644 --- a/src/aws/cloudformation.ts +++ b/src/aws/cloudformation.ts @@ -59,28 +59,30 @@ const postPrecessGenerator = ( const customGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws cloudformation ${command}`; + let args = ["cloudformation", command]; - for (let i = 0; i < options.length; i++) { - const option = options[i]; + for (const option of options) { const idx = tokens.indexOf(option); if (idx < 0) { continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -106,7 +108,7 @@ const customGenerator = async ( const customGeneratorWithFilter = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, @@ -114,21 +116,23 @@ const customGeneratorWithFilter = async ( filter: string ): Promise => { try { - let cmd = `aws cloudformation ${command}`; + let args = ["cloudformation", command]; - for (let i = 0; i < options.length; i++) { - const option = options[i]; + for (const option of options) { const idx = tokens.indexOf(option); if (idx < 0) { continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; return list .filter((resource) => { @@ -147,12 +151,12 @@ const customGeneratorWithFilter = async ( const _prefixFile = "file://"; const _prefixS3 = "s3://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -167,7 +171,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -273,7 +277,7 @@ const generators: Record = { listRemoteFilesGenerator: { script: (tokens) => { const whatHasUserTyped = tokens[tokens.length - 1]; - const baseLSCommand = "\\aws s3 ls "; + const baseLsCommand = ["aws", "s3", "ls"]; let folderPath = ""; @@ -284,17 +288,17 @@ const generators: Record = { // then we can assume that the filepath generator is in work // so do not return any s3 related filepaths if (!_prefixS3.startsWith(whatHasUserTyped)) { - return ""; + return undefined; } - return "echo 's3://'"; + return ["echo", "s3://"]; } if (lastSlashIndex > -1) { folderPath = whatHasUserTyped.slice(0, lastSlashIndex + 1); } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }, postProcess: (out) => { if (out == "") { @@ -373,7 +377,7 @@ const generators: Record = { }, listCfnStackIds: { - script: "aws cloudformation list-stacks", + script: ["aws", "cloudformation", "list-stacks"], postProcess: (out) => { return postPrecessGenerator(out, "StackSummaries", "StackId"); }, @@ -434,42 +438,42 @@ const generators: Record = { }, listCfnStackSets: { - script: "aws cloudformation list-stack-sets", + script: ["aws", "cloudformation", "list-stack-sets"], postProcess: (out) => { return postPrecessGenerator(out, "Summaries", "StackSetId"); }, }, listCfnChangeSets: { - script: "aws cloudformation list-change-sets", + script: ["aws", "cloudformation", "list-change-sets"], postProcess: (out) => { return postPrecessGenerator(out, "Summaries", "ChangeSetId"); }, }, listRoleArns: { - script: "aws iam list-roles --page-size 100", + script: ["aws", "iam", "list-roles", "--page-size", "100"], postProcess: (out) => { return postPrecessGenerator(out, "Roles", "Arn"); }, }, listRoles: { - script: "aws iam list-roles --page-size 100", + script: ["aws", "iam", "list-roles", "--page-size", "100"], postProcess: (out) => { return postPrecessGenerator(out, "Roles", "RoleName"); }, }, listSNSTopics: { - script: "aws sns list-topics", + script: ["aws", "sns", "list-topics"], postProcess: (out) => { return postPrecessGenerator(out, "Topics", "TopicArn"); }, }, getAccountId: { - script: "aws sts get-caller-identity", + script: ["aws", "sts", "get-caller-identity"], postProcess: function (out) { try { const accountId = JSON.parse(out)["Account"]; @@ -482,7 +486,7 @@ const generators: Record = { }, listTypeArns: { - script: "aws cloudformation list-types", + script: ["aws", "cloudformation", "list-types"], postProcess: function (out) { return postPrecessGenerator(out, "TypeSummaries", "TypeArn"); }, @@ -522,10 +526,17 @@ const generators: Record = { return []; } const param = tokens[idx + 1]; - const cmd = `aws cloudformation describe-change-set --change-set-name ${param}`; - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args: [ + "cloudformation", + "describe-change-set", + "--change-set-name", + param, + ], + }); - const stackId = JSON.parse(out)["StackId"]; + const stackId = JSON.parse(stdout)["StackId"]; return [ { @@ -541,14 +552,14 @@ const generators: Record = { }, listExportNames: { - script: "aws cloudformation list-exports", + script: ["aws", "cloudformation", "list-exports"], postProcess: function (out) { return postPrecessGenerator(out, "Exports", "Name"); }, }, listBuckets: { - script: "aws s3 ls --page-size 1000", + script: ["aws", "s3", "ls", "--page-size", "1000"], postProcess: function (out, tokens) { try { return out.split("\n").map((line) => { @@ -569,7 +580,7 @@ const generators: Record = { }, listKmsKeys: { - script: "aws kms list-keys --page-size 100", + script: ["aws", "kms", "list-keys", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Keys", "KeyId"); }, diff --git a/src/aws/cloudwatch.ts b/src/aws/cloudwatch.ts index dea4189108bc..e5274499ccc0 100644 --- a/src/aws/cloudwatch.ts +++ b/src/aws/cloudwatch.ts @@ -303,12 +303,12 @@ const getResultList = async ( const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -323,7 +323,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -415,28 +415,28 @@ const generators: Record = { }, listAlarms: { - script: "aws cloudwatch describe-alarms", + script: ["aws", "cloudwatch", "describe-alarms"], postProcess: (out) => { return postPrecessGenerator(out, "MetricAlarms", "AlarmName"); }, }, listAlarmArns: { - script: "aws cloudwatch describe-alarms", + script: ["aws", "cloudwatch", "describe-alarms"], postProcess: (out) => { return postPrecessGenerator(out, "MetricAlarms", "AlarmArn"); }, }, listAdNamespaces: { - script: "aws cloudwatch describe-anomaly-detectors", + script: ["aws", "cloudwatch", "describe-anomaly-detectors"], postProcess: (out) => { return postPrecessGenerator(out, "AnomalyDetectors", "Namespace"); }, }, listMetricNamespaces: { - script: "aws cloudwatch list-metrics", + script: ["aws", "cloudwatch", "list-metrics"], postProcess: (out) => { return postPrecessGenerator(out, "Metrics", "Namespace"); }, @@ -495,35 +495,35 @@ const generators: Record = { }, listDashboards: { - script: "aws cloudwatch list-dashboards", + script: ["aws", "cloudwatch", "list-dashboards"], postProcess: (out) => { return postPrecessGenerator(out, "DashboardEntries", "DashboardName"); }, }, listInsightRules: { - script: "aws cloudwatch describe-insight-rules", + script: ["aws", "cloudwatch", "describe-insight-rules"], postProcess: (out) => { return postPrecessGenerator(out, "InsightRules", "Name"); }, }, listMetricStreams: { - script: "aws cloudwatch list-metric-streams", + script: ["aws", "cloudwatch", "list-metric-streams"], postProcess: (out) => { return postPrecessGenerator(out, "Entries", "Name"); }, }, listMetrics: { - script: "aws cloudwatch list-metrics", + script: ["aws", "cloudwatch", "list-metrics"], postProcess: (out) => { return postPrecessGenerator(out, "Metrics", "MetricName"); }, }, listSNSTopics: { - script: "aws sns list-topics", + script: ["aws", "sns", "list-topics"], postProcess: (out) => { return postPrecessGenerator(out, "Topics", "TopicArn"); }, @@ -561,7 +561,7 @@ const generators: Record = { }, listRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => { return postPrecessGenerator(out, "Roles", "Arn"); }, diff --git a/src/aws/ec2.ts b/src/aws/ec2.ts index f8bd84be1bb1..73d451247169 100644 --- a/src/aws/ec2.ts +++ b/src/aws/ec2.ts @@ -12,127 +12,261 @@ const postProcessAWS: Fig.Generator["postProcess"] = (out) => { }; const awsGenerators: Record = { instances: { - script: - "aws ec2 describe-instances --query 'Reservations[*].Instances[].InstanceId'", + script: [ + "aws", + "ec2", + "describe-instances", + "--query", + "Reservations[*].Instances[].InstanceId", + ], postProcess: postProcessAWS, }, rtb: { - script: - "aws ec2 describe-route-tables --query 'RouteTables[*].RouteTableId'", + script: [ + "aws", + "ec2", + "describe-route-tables", + "--query", + "RouteTables[*].RouteTableId", + ], postProcess: postProcessAWS, }, start: { - script: - "aws ec2 describe-instances --filters 'Name=instance-state-name,Values=stopped' --query 'Reservations[*].Instances[].InstanceId'", + script: [ + "aws", + "ec2", + "describe-instances", + "--filters", + "Name=instance-state-name,Values=stopped", + "--query", + "Reservations[*].Instances[].InstanceId", + ], postProcess: postProcessAWS, }, stop: { - script: - "aws ec2 describe-instances --filters 'Name=instance-state-name,Values=running' --query 'Reservations[*].Instances[].InstanceId'", + script: [ + "aws", + "ec2", + "describe-instances", + "--filters", + "Name=instance-state-name,Values=running", + "--query", + "Reservations[*].Instances[].InstanceId", + ], postProcess: postProcessAWS, }, volume_id: { - script: "aws ec2 describe-volumes --query 'Volumes[*].VolumeId'", + script: [ + "aws", + "ec2", + "describe-volumes", + "--query", + "Volumes[*].VolumeId", + ], postProcess: postProcessAWS, }, reserved_instance_id: { - script: - "aws ec2 describe-reserved-instances --query 'ReservedInstances[*].ReservedInstancesId'", + script: [ + "aws", + "ec2", + "describe-reserved-instances", + "--query", + "ReservedInstances[*].ReservedInstancesId", + ], postProcess: postProcessAWS, }, transit_gateway_multicast_domain_id: { - script: - "aws ec2 describe-transit-gateway-multicast-domains --query 'TransitGatewayMulticastDomains[*].TransitGatewayMulticastDomainId'", + script: [ + "aws", + "ec2", + "describe-transit-gateway-multicast-domains", + "--query", + "TransitGatewayMulticastDomains[*].TransitGatewayMulticastDomainId", + ], postProcess: postProcessAWS, }, transit_gateway_attachment_id: { - script: - "aws ec2 describe-transit-gateway-attachments --query 'TransitGatewayAttachments[*].TransitGatewayAttachmentId'", + script: [ + "aws", + "ec2", + "describe-transit-gateway-attachments", + "--query", + "TransitGatewayAttachments[*].TransitGatewayAttachmentId", + ], postProcess: postProcessAWS, }, vpc_id: { - script: "aws ec2 describe-vpc-endpoints --query 'VpcEndpoints[*].VpcId'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoints", + "--query", + "VpcEndpoints[*].VpcId", + ], postProcess: postProcessAWS, }, vpc_endpoint_id: { - script: - "aws ec2 describe-vpc-endpoints --query 'VpcEndpoints[*].VpcEndpointId'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoints", + "--query", + "VpcEndpoints[*].VpcEndpointId", + ], postProcess: postProcessAWS, }, subnet_ids: { - script: - "aws ec2 describe-vpc-endpoints --query 'VpcEndpoints[*].SubnetIds'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoints", + "--query", + "VpcEndpoints[*].SubnetIds", + ], postProcess: postProcessAWS, }, route_table_ids: { - script: - "aws ec2 describe-vpc-endpoints --query 'VpcEndpoints[*].RouteTableIds'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoints", + "--query", + "VpcEndpoints[*].RouteTableIds", + ], postProcess: postProcessAWS, }, network_interface_ids: { - script: - "aws ec2 describe-vpc-endpoints --query 'VpcEndpoints[*].NetworkInterfaceIds'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoints", + "--query", + "VpcEndpoints[*].NetworkInterfaceIds", + ], postProcess: postProcessAWS, }, instance_type: { - script: - "aws ec2 describe-instance-types --query 'InstanceTypes[*].InstanceType'", + script: [ + "aws", + "ec2", + "describe-instance-types", + "--query", + "InstanceTypes[*].InstanceType", + ], postProcess: postProcessAWS, }, snapshot_ids: { - script: "aws ec2 describe-snapshots --query 'Snapshots[*].SnapshotId'", + script: [ + "aws", + "ec2", + "describe-snapshots", + "--query", + "Snapshots[*].SnapshotId", + ], postProcess: postProcessAWS, }, vpc_peering_connection_id: { - script: - "aws ec2 describe-vpc-peering-connections --query 'VpcPeeringConnections[*].VpcPeeringConnectionId'", + script: [ + "aws", + "ec2", + "describe-vpc-peering-connections", + "--query", + "VpcPeeringConnections[*].VpcPeeringConnectionId", + ], postProcess: postProcessAWS, }, service_id: { - script: - "aws ec2 describe-vpc-endpoint-services --query 'ServiceDetails[*].ServiceId'", + script: [ + "aws", + "ec2", + "describe-vpc-endpoint-services", + "--query", + "ServiceDetails[*].ServiceId", + ], postProcess: postProcessAWS, }, cidr_block: { - script: "aws ec2 describe-subnets --query 'Subnets[*].CidrBlock'", + script: [ + "aws", + "ec2", + "describe-subnets", + "--query", + "Subnets[*].CidrBlock", + ], postProcess: postProcessAWS, }, image_id: { - script: "aws ec2 describe-images --query 'Images[*].ImageId'", + script: ["aws", "ec2", "describe-images", "--query", "Images[*].ImageId"], postProcess: postProcessAWS, }, key_pair: { - script: "aws ec2 describe-key-pairs --query 'KeyPairs[*].KeyName'", + script: [ + "aws", + "ec2", + "describe-key-pairs", + "--query", + "KeyPairs[*].KeyName", + ], postProcess: postProcessAWS, }, internet_gateway_id: { - script: - "aws ec2 describe-internet-gateways --query 'InternetGateways[*].InternetGatewayId'", + script: [ + "aws", + "ec2", + "describe-internet-gateways", + "--query", + "InternetGateways[*].InternetGatewayId", + ], postProcess: postProcessAWS, }, region_name: { - script: - "aws ec2 describe-availability-zones --query 'AvailabilityZones[*].RegionName'", + script: [ + "aws", + "ec2", + "describe-availability-zones", + "--query", + "AvailabilityZones[*].RegionName", + ], postProcess: postProcessAWS, }, zone_name: { - script: - "aws ec2 describe-availability-zones --query 'AvailabilityZones[*].ZoneName'", + script: [ + "aws", + "ec2", + "describe-availability-zones", + "--query", + "AvailabilityZones[*].ZoneName", + ], postProcess: postProcessAWS, }, zone_id: { - script: - "aws ec2 describe-availability-zones --query 'AvailabilityZones[*].ZoneId'", + script: [ + "aws", + "ec2", + "describe-availability-zones", + "--query", + "AvailabilityZones[*].ZoneId", + ], postProcess: postProcessAWS, }, group_name: { - script: - "aws ec2 describe-availability-zones --query 'AvailabilityZones[*].GroupName'", + script: [ + "aws", + "ec2", + "describe-availability-zones", + "--query", + "AvailabilityZones[*].GroupName", + ], postProcess: postProcessAWS, }, network_border_group: { - script: - "aws ec2 describe-availability-zones --query 'AvailabilityZones[*].NetworkBorderGroup'", + script: [ + "aws", + "ec2", + "describe-availability-zones", + "--query", + "AvailabilityZones[*].NetworkBorderGroup", + ], postProcess: postProcessAWS, }, }; diff --git a/src/aws/ecs.ts b/src/aws/ecs.ts index 92abed3bdd56..87cbc7ac5b11 100644 --- a/src/aws/ecs.ts +++ b/src/aws/ecs.ts @@ -57,14 +57,14 @@ const postPrecessGenerator = ( const customGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws ecs ${command}`; + let args = ["ecs", command]; for (const option of options) { const idx = tokens.indexOf(option); @@ -72,12 +72,15 @@ const customGenerator = async ( continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -103,22 +106,29 @@ const customGenerator = async ( const MultiSuggestionsGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - enabled: Record[] + executeShellCommand: Fig.ExecuteCommandFunction, + enabled: { + command: string[]; + parentKey: string; + childKey: string; + }[] ) => { try { const list: Fig.Suggestion[][] = []; const promises: Promise[] = []; for (let i = 0; i < enabled.length; i++) { - promises[i] = executeShellCommand(enabled[i]["command"]); + promises[i] = executeShellCommand({ + command: "aws", + args: enabled[i].command, + }).then(({ stdout }) => stdout); } const result = await Promise.all(promises); for (let i = 0; i < enabled.length; i++) { list[i] = postPrecessGenerator( result[i], - enabled[i]["parentKey"], - enabled[i]["childKey"] + enabled[i].parentKey, + enabled[i].childKey ); } @@ -131,12 +141,12 @@ const MultiSuggestionsGenerator = async ( const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -151,7 +161,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -255,18 +265,18 @@ const generators = { }, listCapacityProviders: { - script: "aws ecs describe-capacity-providers", + script: ["aws", "ecs", "describe-capacity-providers"], postProcess: (out) => postPrecessGenerator(out, "capacityProviders", "name"), }, listClusters: { - script: "aws ecs list-clusters", + script: ["aws", "ecs", "list-clusters"], postProcess: (out) => postPrecessGenerator(out, "clusterArns"), }, listTaskDefinitions: { - script: "aws ecs list-task-definitions", + script: ["aws", "ecs", "list-task-definitions"], postProcess: (out) => postPrecessGenerator(out, "taskDefinitionArns"), }, @@ -283,32 +293,38 @@ const generators = { }, listRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => postPrecessGenerator(out, "Roles", "RoleName"), }, listServices: { - script: "aws ecs list-services", + script: ["aws", "ecs", "list-services"], postProcess: (out) => postPrecessGenerator(out, "serviceArns"), }, listUsers: { - script: "aws iam list-users", + script: ["aws", "iam", "list-users"], postProcess: (out) => postPrecessGenerator(out, "Users", "Arn"), }, listContainerInstances: { - script: "aws ecs list-container-instances", + script: ["aws", "ecs", "list-container-instances"], postProcess: (out) => postPrecessGenerator(out, "containerInstanceArns"), }, listTasks: { - script: "aws ecs list-tasks", + script: ["aws", "ecs", "list-tasks"], postProcess: (out) => postPrecessGenerator(out, "taskArns"), }, listAttributeNames: { - script: "aws ecs list-attributes --target-type container-instance", + script: [ + "aws", + "ecs", + "list-attributes", + "--target-type", + "container-instance", + ], postProcess: (out) => postPrecessGenerator(out, "attributes", "name"), }, @@ -325,7 +341,7 @@ const generators = { }, listTaskDefinitionFamilies: { - script: "aws ecs list-task-definition-families", + script: ["aws", "ecs", "list-task-definition-families"], postProcess: (out) => postPrecessGenerator(out, "families"), }, @@ -334,7 +350,7 @@ const generators = { const out = await executeShellCommand("aws ecs list-tasks"); const list = JSON.parse(out)["taskArns"]; const tasks = list.map((arn) => ({ - command: `aws ecs describe-tasks --tasks ${arn}`, + command: ["ecs", "describe-tasks", "--tasks", arn], parentKey: "tasks", childKey: "startedBy", })); @@ -355,7 +371,7 @@ const generators = { ); const list = JSON.parse(out)["taskArns"]; const tasks = list.map((arn) => ({ - command: `aws ecs describe-tasks --tasks ${arn}`, + command: ["ecs", "describe-tasks", "--tasks", arn], parentKey: "tasks", childKey: "group", })); @@ -368,7 +384,7 @@ const generators = { customGenerator( out, executeShellCommand, - "aws ecs list-tags-for-resource", + "list-tags-for-resource", ["--resource-arn"], "tags", "key" @@ -376,7 +392,7 @@ const generators = { }, listCodedeployApplications: { - script: "aws deploy list-applications", + script: ["aws", "deploy", "list-applications"], postProcess: (out) => postPrecessGenerator(out, "applications"), }, diff --git a/src/aws/eks.ts b/src/aws/eks.ts index cf051643a8f4..7cab444f6666 100644 --- a/src/aws/eks.ts +++ b/src/aws/eks.ts @@ -108,12 +108,12 @@ const listRolesForPrincipal = ( const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -128,7 +128,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -220,12 +220,12 @@ const generators: Record = { }, listClusters: { - script: "aws eks list-clusters", + script: ["aws", "eks", "list-clusters"], postProcess: (out) => postPrecessGenerator(out, "clusters"), }, listKmsKeys: { - script: "aws kms list-keys", + script: ["aws", "kms", "list-keys"], postProcess: function (out) { try { const list = JSON.parse(out)["Keys"]; @@ -255,12 +255,12 @@ const generators: Record = { }, listAddons: { - script: "aws eks describe-addon-versions", + script: ["aws", "eks", "describe-addon-versions"], postProcess: (out) => postPrecessGenerator(out, "addons", "addonName"), }, listAddonVersions: { - script: "aws eks describe-addon-versions", + script: ["aws", "eks", "describe-addon-versions"], postProcess: (out) => { try { const addons = JSON.parse(out)["addons"]; @@ -283,23 +283,23 @@ const generators: Record = { }, listRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => postPrecessGenerator(out, "Roles", "RoleName"), }, listEKSClusterRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => listRolesForPrincipal(out, "eks.amazonaws.com"), }, listFargatePodRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => listRolesForPrincipal(out, "eks-fargate-pods.amazonaws.com"), }, listNodeGroupRoles: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => listRolesForPrincipal(out, "eks-nodegroup.amazonaws.com"), }, diff --git a/src/aws/elasticbeanstalk.ts b/src/aws/elasticbeanstalk.ts index 9df9012fa6bc..23d65247491b 100644 --- a/src/aws/elasticbeanstalk.ts +++ b/src/aws/elasticbeanstalk.ts @@ -39,27 +39,31 @@ const postPrecessGenerator = ( const customGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - command: string, + executeShellCommand: Fig.ExecuteCommandFunction, + command: string[], options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws elasticbeanstalk ${command}`; - for (let i = 0; i < options.length; i++) { - const option = options[i]; + let args = ["elasticbeanstalk", ...command]; + + for (const option of options) { const idx = tokens.indexOf(option); if (idx < 0) { continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); + + const list = JSON.parse(stdout)[parentKey]; - const list = JSON.parse(out)[parentKey]; if (!Array.isArray(list)) { return [ { @@ -84,7 +88,7 @@ const customGenerator = async ( const filterManagedAction = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, @@ -94,7 +98,7 @@ const filterManagedAction = async ( return customGenerator( tokens, executeShellCommand, - `${command} --status ${filter}`, + [command, "--status", filter], options, parentKey, childKey @@ -103,12 +107,12 @@ const filterManagedAction = async ( const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -123,7 +127,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -212,14 +216,14 @@ const generators: Record = { }, listEnvironmentIds: { - script: "aws elasticbeanstalk describe-environments", + script: ["aws", "elasticbeanstalk", "describe-environments"], postProcess: (out) => { return postPrecessGenerator(out, "Environments", "EnvironmentId"); }, }, listEnvironmentNames: { - script: "aws elasticbeanstalk describe-environments", + script: ["aws", "elasticbeanstalk", "describe-environments"], postProcess: (out) => { return postPrecessGenerator(out, "Environments", "EnvironmentName"); }, @@ -240,14 +244,14 @@ const generators: Record = { }, listIamRoleArns: { - script: "aws iam list-roles", + script: ["aws", "iam", "list-roles"], postProcess: (out) => { return postPrecessGenerator(out, "Roles", "Arn"); }, }, listCnamePrefixes: { - script: "aws elasticbeanstalk describe-environments", + script: ["aws", "elasticbeanstalk", "describe-environments"], postProcess: (out) => { return postPrecessGenerator(out, "Environments", "CNAME").map((cname) => { try { @@ -267,7 +271,7 @@ const generators: Record = { }, listApplications: { - script: "aws elasticbeanstalk describe-applications", + script: ["aws", "elasticbeanstalk", "describe-applications"], postProcess: (out) => { return postPrecessGenerator(out, "Applications", "ApplicationName"); }, @@ -278,7 +282,7 @@ const generators: Record = { return customGenerator( tokens, executeShellCommand, - "describe-application-versions", + ["describe-application-versions"], ["--application-name"], "ApplicationVersions", "VersionLabel" @@ -287,7 +291,7 @@ const generators: Record = { }, listBuckets: { - script: "aws s3 ls --page-size 1000", + script: ["aws", "s3", "ls", "--page-size", "1000"], postProcess: (out) => { try { return out.split("\n").map((line) => { @@ -310,28 +314,28 @@ const generators: Record = { }, listSolutionStacks: { - script: "aws elasticbeanstalk list-available-solution-stacks", + script: ["aws", "elasticbeanstalk", "list-available-solution-stacks"], postProcess: (out) => { return postPrecessGenerator(out, "SolutionStacks"); }, }, listPlatformArns: { - script: "aws elasticbeanstalk list-platform-versions", + script: ["aws", "elasticbeanstalk", "list-platform-versions"], postProcess: (out) => { return postPrecessGenerator(out, "PlatformSummaryList", "PlatformArn"); }, }, listApplicationArns: { - script: "aws elasticbeanstalk describe-applications", + script: ["aws", "elasticbeanstalk", "describe-applications"], postProcess: (out) => { return postPrecessGenerator(out, "Applications", "ApplicationArn"); }, }, listEnvironmentArns: { - script: "aws elasticbeanstalk describe-environments", + script: ["aws", "elasticbeanstalk", "describe-environments"], postProcess: (out) => { return postPrecessGenerator(out, "Environments", "EnvironmentArn"); }, diff --git a/src/aws/iam.ts b/src/aws/iam.ts index 0e5ced002af4..2d36c6f2107e 100644 --- a/src/aws/iam.ts +++ b/src/aws/iam.ts @@ -195,12 +195,12 @@ const identityStruct: Identity[] = [ const _prefixFile = "file://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -215,7 +215,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -357,7 +357,7 @@ const MultiSuggestionsGenerator = async ( const generators: Record = { getAccountArn: { - script: "aws sts get-caller-identity", + script: ["aws", "sts", "get-caller-identity"], postProcess: function (out, tokens) { try { const accountId = JSON.parse(out)["Account"]; @@ -373,7 +373,7 @@ const generators: Record = { }, listOpenIdProviders: { - script: "aws iam list-open-id-connect-providers", + script: ["aws", "iam", "list-open-id-connect-providers"], postProcess: function (out) { return postPrecessGenerator(out, "OpenIDConnectProviderList", "Arn"); }, @@ -413,7 +413,7 @@ const generators: Record = { }, listInstanceProfiles: { - script: "aws iam list-instance-profiles --page-size 100", + script: ["aws", "iam", "list-instance-profiles", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator( out, @@ -455,7 +455,7 @@ const generators: Record = { }, listUsers: { - script: "aws iam list-users --page-size 100", + script: ["aws", "iam", "list-users", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Users", "UserName"); }, @@ -465,7 +465,7 @@ const generators: Record = { }, listUserArns: { - script: "aws iam list-users --page-size 100", + script: ["aws", "iam", "list-users", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Users", "Arn"); }, @@ -490,7 +490,7 @@ const generators: Record = { }, listGroups: { - script: "aws iam list-groups --page-size 100", + script: ["aws", "iam", "list-groups", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Groups", "GroupName"); }, @@ -516,7 +516,15 @@ const generators: Record = { }, listIamPoliciesArn: { - script: "aws iam list-policies --page-size 100 --scope Local", + script: [ + "aws", + "iam", + "list-policies", + "--page-size", + "100", + "--scope", + "Local", + ], postProcess: function (out) { return postPrecessGenerator(out, "Policies", "Arn"); }, @@ -621,7 +629,7 @@ const generators: Record = { }, listRoles: { - script: "aws iam list-roles --page-size 100", + script: ["aws", "iam", "list-roles", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Roles", "RoleName"); }, @@ -666,7 +674,7 @@ const generators: Record = { }, listMfaDevices: { - script: "aws iam list-mfa-devices --page-size 100", + script: ["aws", "iam", "list-mfa-devices", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "MFADevices", "SerialNumber"); }, @@ -676,7 +684,7 @@ const generators: Record = { }, listVirtualMfaDevices: { - script: "aws iam list-virtual-mfa-devices --page-size 100", + script: ["aws", "iam", "list-virtual-mfa-devices", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "VirtualMFADevices", "SerialNumber"); }, @@ -686,7 +694,7 @@ const generators: Record = { }, listAccessKeyIds: { - script: "aws iam list-access-keys --page-size 100", + script: ["aws", "iam", "list-access-keys", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "AccessKeyMetadata", "AccessKeyId"); }, @@ -696,7 +704,7 @@ const generators: Record = { }, listAccountAliases: { - script: "aws iam list-account-aliases --page-size 100", + script: ["aws", "iam", "list-account-aliases", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "AccountAliases"); }, @@ -706,7 +714,7 @@ const generators: Record = { }, listSamlProviders: { - script: "aws iam list-saml-providers", + script: ["aws", "iam", "list-saml-providers"], postProcess: function (out) { return postPrecessGenerator(out, "SAMLProviderList", "Arn"); }, @@ -716,7 +724,7 @@ const generators: Record = { }, listSSHPublicKeys: { - script: "aws iam list-ssh-public-keys --page-size 1000", + script: ["aws", "iam", "list-ssh-public-keys", "--page-size", "1000"], postProcess: function (out) { return postPrecessGenerator(out, "SSHPublicKeys", "SSHPublicKeyId"); }, @@ -742,7 +750,7 @@ const generators: Record = { }, listServerCerts: { - script: "aws iam list-server-certificates --page-size 1000", + script: ["aws", "iam", "list-server-certificates", "--page-size", "1000"], postProcess: function (out) { return postPrecessGenerator( out, diff --git a/src/aws/lambda.ts b/src/aws/lambda.ts index ddd9f3c0f341..5ea223f26e7f 100644 --- a/src/aws/lambda.ts +++ b/src/aws/lambda.ts @@ -264,14 +264,14 @@ const postPrecessGenerator = ( const listCustomGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws lambda ${command}`; + let args = ["lambda", command]; for (let i = 0; i < options.length; i++) { const option = options[i]; @@ -280,12 +280,15 @@ const listCustomGenerator = async ( continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -309,24 +312,14 @@ const listCustomGenerator = async ( return []; }; -const getResultList = async ( - tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - command: string, - key: string -): Promise => { - const out = await executeShellCommand(command); - return JSON.parse(out)[key]; -}; - const listCustomSIDGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[] ): Promise => { try { - let cmd = `aws lambda ${command}`; + let args = ["lambda", command]; for (let i = 0; i < options.length; i++) { const option = options[i]; @@ -335,12 +328,15 @@ const listCustomSIDGenerator = async ( continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const policies = JSON.parse(out)["Policy"]; + const policies = JSON.parse(stdout)["Policy"]; const statement = JSON.parse(policies)["Statement"]; return statement.map((elm) => { return { @@ -356,22 +352,29 @@ const listCustomSIDGenerator = async ( const MultiSuggestionsGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - enabled: Record[] + executeShellCommand: Fig.ExecuteCommandFunction, + enabled: { + command: string[]; + parentKey: string; + childKey: string; + }[] ) => { try { const list: Fig.Suggestion[][] = []; const promises: Promise[] = []; for (let i = 0; i < enabled.length; i++) { - promises[i] = executeShellCommand(enabled[i]["command"]); + promises[i] = executeShellCommand({ + command: "aws", + args: enabled[i].command, + }).then(({ stdout }) => stdout); } const result = await Promise.all(promises); for (let i = 0; i < enabled.length; i++) { list[i] = postPrecessGenerator( result[i], - enabled[i]["parentKey"], - enabled[i]["childKey"] + enabled[i].parentKey, + enabled[i].childKey ); } @@ -386,12 +389,12 @@ const _prefixFile = "file://"; const _prefixBlob = "fileb://"; const _prefixS3 = "s3://"; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; if (!whatHasUserTyped.startsWith(prefix)) { - return `echo '${prefix}'`; + return ["echo", prefix]; } whatHasUserTyped = whatHasUserTyped.slice(prefix.length); @@ -406,7 +409,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -515,7 +518,7 @@ const generators: Record = { }, listLayerArns: { - script: "aws lambda list-layers", + script: ["aws", "lambda", "list-layers"], postProcess: (out) => { return postPrecessGenerator(out, "Layers", "LayerArn"); }, @@ -541,7 +544,7 @@ const generators: Record = { }, getPrincipal: { - script: "aws sts get-caller-identity", + script: ["aws", "sts", "get-caller-identity"], postProcess: function (out, tokens) { try { const accountId = JSON.parse(out)["Account"]; @@ -603,7 +606,7 @@ const generators: Record = { }, listLambdaFunctions: { - script: "aws lambda list-functions", + script: ["aws", "lambda", "list-functions"], postProcess: (out) => { return postPrecessGenerator(out, "Functions", "FunctionArn"); }, @@ -638,13 +641,19 @@ const generators: Record = { custom: async function (tokens, executeShellCommand) { try { const idx = tokens.indexOf("--function-name"); - const cmd = `aws lambda list-versions-by-function --function-name ${ - tokens[idx + 1] - }`; + const args = [ + "lambda", + "list-versions-by-function", + "--function-name", + tokens[idx + 1], + ]; - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)["Versions"]; + const list = JSON.parse(stdout)["Versions"]; return list .filter((elm) => elm.Version !== "$LATEST") .map((elm) => { @@ -720,12 +729,12 @@ const generators: Record = { return MultiSuggestionsGenerator(tokens, executeShellCommand, [ { - command: "aws dynamodbstreams list-streams", + command: ["dynamodbstreams", "list-streams"], parentKey: "Streams", childKey: "StreamArn", }, { - command: "aws kafka list-clusters", + command: ["kafka", "list-clusters"], parentKey: "ClusterInfoList", childKey: "ClusterArn", }, @@ -753,17 +762,17 @@ const generators: Record = { return MultiSuggestionsGenerator(tokens, executeShellCommand, [ { - command: "aws sns list-topics", + command: ["sns", "list-topics"], parentKey: "Topics", childKey: "TopicArn", }, { - command: "aws events list-event-buses", + command: ["events", "list-event-buses"], parentKey: "EventBuses", childKey: "Arn", }, { - command: "aws lambda list-functions", + command: ["lambda", "list-functions"], parentKey: "Functions", childKey: "FunctionArn", }, @@ -776,7 +785,7 @@ const generators: Record = { }, listRoles: { - script: "aws iam list-roles --page-size 100", + script: ["aws", "iam", "list-roles", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Roles", "RoleName"); }, @@ -786,7 +795,7 @@ const generators: Record = { }, listKmsKeys: { - script: "aws kms list-keys --page-size 100", + script: ["aws", "kms", "list-keys", "--page-size", "100"], postProcess: function (out) { return postPrecessGenerator(out, "Keys", "KeyArn"); }, @@ -796,7 +805,7 @@ const generators: Record = { }, listLayers: { - script: "aws lambda list-layers", + script: ["aws", "lambda", "list-layers"], postProcess: function (out) { return postPrecessGenerator(out, "Layers", "LayerArn"); }, @@ -806,7 +815,7 @@ const generators: Record = { }, listLayerArnsWithVersion: { - script: "aws lambda list-layers", + script: ["aws", "lambda", "list-layers"], postProcess: function (out) { try { const list = JSON.parse(out)["Layers"]; @@ -827,7 +836,7 @@ const generators: Record = { }, listFilesystemConfigs: { - script: "aws efs describe-file-systems", + script: ["aws", "efs", "describe-file-systems"], postProcess: function (out) { try { const list = JSON.parse(out)["FileSystems"]; @@ -849,7 +858,13 @@ const generators: Record = { }, listCodeSigningConfigs: { - script: "aws lambda list-code-signing-configs --page-size 100", + script: [ + "aws", + "lambda", + "list-code-signing-configs", + "--page-size", + "100", + ], postProcess: function (out) { return postPrecessGenerator( out, @@ -863,7 +878,13 @@ const generators: Record = { }, listEventSourceMappingUUIDs: { - script: "aws lambda list-event-source-mappings --page-size 100", + script: [ + "aws", + "lambda", + "list-event-source-mappings", + "--page-size", + "100", + ], postProcess: function (out) { return postPrecessGenerator(out, "EventSourceMappings", "UUID"); }, @@ -873,7 +894,7 @@ const generators: Record = { }, listCodeSHA: { - script: "aws lambda list-functions", + script: ["aws", "lambda", "list-functions"], postProcess: function (out) { return postPrecessGenerator(out, "Functions", "CodeSha256"); }, @@ -883,7 +904,7 @@ const generators: Record = { }, listBuckets: { - script: "aws s3 ls --page-size 1000", + script: ["aws", "s3", "ls", "--page-size", "1000"], postProcess: function (out, tokens) { try { return out.split("\n").map((line) => { @@ -910,17 +931,25 @@ const generators: Record = { custom: async function (tokens, executeShellCommand) { try { const idx = tokens.indexOf("--s3-bucket"); - const cmd = `aws s3 ls ${_prefixS3}${ - tokens[idx + 1] - } --recursive --page-size 1000`; + const args = [ + "s3", + "ls", + `${_prefixS3}${tokens[idx + 1]}`, + "--recursive", + "--page-size", + "1000", + ]; - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - if (out == "") { + if (stdout == "") { return []; } - if (out.trim() === _prefixS3) { + if (stdout.trim() === _prefixS3) { return [ { name: _prefixS3, @@ -929,7 +958,7 @@ const generators: Record = { ]; } - return out.split("\n").map((line) => { + return stdout.split("\n").map((line) => { const parts = line.split(/\s+/); // sub prefix if (!parts.length) { @@ -954,17 +983,25 @@ const generators: Record = { try { const bucketIdx = tokens.indexOf("--s3-bucket"); const objectIdx = tokens.indexOf("--s3-key"); - const cmd = `aws s3api list-object-versions --bucket ${ - tokens[bucketIdx + 1] - } --prefix ${tokens[objectIdx + 1]}`; + const args = [ + "s3api", + "list-object-versions", + "--bucket", + tokens[bucketIdx + 1], + "--prefix", + tokens[objectIdx + 1], + ]; - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - if (out == "") { + if (stdout == "") { return []; } - if (out.trim() === _prefixS3) { + if (stdout.trim() === _prefixS3) { return [ { name: _prefixS3, @@ -973,7 +1010,7 @@ const generators: Record = { ]; } - const list = JSON.parse(out)["Versions"]; + const list = JSON.parse(stdout)["Versions"]; return list .filter((elm) => elm["VersionId"] !== "null") .map((elm) => { diff --git a/src/aws/s3.ts b/src/aws/s3.ts index 897a469322eb..a4ce53808b32 100644 --- a/src/aws/s3.ts +++ b/src/aws/s3.ts @@ -319,7 +319,7 @@ const generators: Record = { // just bucket names listBuckets: { - script: "aws s3 ls --page-size 1000", + script: ["aws", "s3", "ls", "--page-size", "1000"], postProcess: function (out, context) { try { return out.split("\n").map((line) => { @@ -345,7 +345,7 @@ const generators: Record = { kmsKeyIdGenerator: { // --page-size does not affect the number of items returned, // just chunks request so it won't timeout - script: "aws kms list-keys --page-size 100", + script: ["aws", "kms", "list-keys", "--page-size", "100"], postProcess: function (out) { try { const list = JSON.parse(out)["Keys"]; diff --git a/src/aws/s3api.ts b/src/aws/s3api.ts index 740dfa0a2c25..d510584d1e31 100644 --- a/src/aws/s3api.ts +++ b/src/aws/s3api.ts @@ -1,5 +1,5 @@ const bucketGenerator: Fig.Generator = { - script: "aws s3api list-buckets", + script: ["aws", "s3api", "list-buckets"], postProcess: function (out) { const json = JSON.parse(out); return json.Buckets.map((bucket) => { diff --git a/src/aws/secretsmanager.ts b/src/aws/secretsmanager.ts index 54c639c46bd5..9360ec0c1106 100644 --- a/src/aws/secretsmanager.ts +++ b/src/aws/secretsmanager.ts @@ -24,15 +24,14 @@ const awsRegions = [ const ttl = 30000; -const appendFolderPath = (tokens: string[], prefix: string): string => { - const baseLSCommand = "\\ls -1ApL "; +const appendFolderPath = (tokens: string[], prefix: string): string[] => { + const baseLsCommand = ["ls", "-1ApL"]; let whatHasUserTyped = tokens[tokens.length - 1]; - if (whatHasUserTyped.startsWith(prefix)) { - whatHasUserTyped = whatHasUserTyped.slice(prefix.length); - } else { - return `echo '${prefix}'`; + if (!whatHasUserTyped.startsWith(prefix)) { + return ["echo", prefix]; } + whatHasUserTyped = whatHasUserTyped.slice(prefix.length); let folderPath = ""; const lastSlashIndex = whatHasUserTyped.lastIndexOf("/"); @@ -45,7 +44,7 @@ const appendFolderPath = (tokens: string[], prefix: string): string => { } } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -123,7 +122,15 @@ const generators: Record = { secretIdsGenerator: { // --page-size does not affect the number of items returned, // just chunks request so it won't timeout - script: "aws secretsmanager list-secrets --sort-order asc --page-size 100", + script: [ + "aws", + "secretsmanager", + "list-secrets", + "--sort-order", + "asc", + "--page-size", + "100", + ], postProcess: function (out) { try { const list = JSON.parse(out)["SecretList"]; @@ -142,7 +149,7 @@ const generators: Record = { kmsKeyIdGenerator: { // --page-size does not affect the number of items returned, // just chunks request so it won't timeout - script: "aws kms list-keys --page-size 100", + script: ["aws", "kms", "list-keys", "--page-size", "100"], postProcess: function (out) { try { const list = JSON.parse(out)["Keys"]; @@ -199,7 +206,7 @@ const generators: Record = { }, }, getReplicaRegionsGenerator: { - script: "aws kms list-keys --page-size 100", + script: ["aws", "kms", "list-keys", "--page-size", "100"], postProcess: function (out, tokens) { try { const list = JSON.parse(out)["Keys"]; @@ -220,7 +227,7 @@ const generators: Record = { }, }, getLambdasGenerator: { - script: "aws lambda list-functions --page-size 100", + script: ["aws", "lambda", "list-functions", "--page-size", "100"], postProcess: function (out, tokens) { try { const list = JSON.parse(out)["Functions"]; diff --git a/src/bat.ts b/src/bat.ts index d45507e56296..9ee29589c985 100644 --- a/src/bat.ts +++ b/src/bat.ts @@ -16,7 +16,7 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --list-languages", + script: ["bat", "--list-languages"], postProcess: function (out) { // unpack 2-dimension array return out @@ -86,7 +86,7 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --wrap unknow 2>&1 >/dev/null | grep possible", + script: "bat --wrap unknow 2>&1 >/dev/null | grep possible", postProcess: function (out) { return out .trim() @@ -251,7 +251,7 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --list-themes", + script: ["bat", "--list-themes"], postProcess: function (out) { return out.split("\n").map((theme) => { return { name: theme, description: "theme: " + theme }; diff --git a/src/bazel.ts b/src/bazel.ts index 243e5e4efad1..bd44e8f22e26 100644 --- a/src/bazel.ts +++ b/src/bazel.ts @@ -1,5 +1,9 @@ const bazelBuildFiles: Fig.Generator = { - script: `FILES=( $(find ./ -name BUILD) ); for f in $FILES; do echo "----$f"; \\cat "$f"; done`, + script: [ + "bash", + "-c", + `FILES=( $(find ./ -name BUILD) ); for f in $FILES; do echo "----$f"; \\cat "$f"; done`, + ], // returns filepaths and contents in the form below, note the "----" to indicate the filepath // ----.//lib/BUILD // load("@rules_cc//cc:defs.bzl", "cc_library") diff --git a/src/black.ts b/src/black.ts index 00e3d421bfe9..33f9dbc599f2 100644 --- a/src/black.ts +++ b/src/black.ts @@ -1,6 +1,6 @@ // https://github.com/psf/black const blackVersions: Fig.Generator = { - script: "gh release list --repo psf/black", + script: ["gh","release","list","--repo","psf/black"], cache: { ttl: 1000 * 60 * 60 * 24 * 2, // 2 days }, diff --git a/src/bosh.ts b/src/bosh.ts index ea9558c3cc7c..8b9612c561fe 100644 --- a/src/bosh.ts +++ b/src/bosh.ts @@ -4,7 +4,7 @@ const genericPathArg: Fig.Arg = { }; const deployments: Fig.Generator = { - script: "bosh --json deployments", + script: ["bosh", "--json", "deployments"], postProcess: function (out) { if (out.startsWith("fatal:")) { return []; diff --git a/src/brew.ts b/src/brew.ts index 9b3ebaf5672b..d5d10d3f978c 100644 --- a/src/brew.ts +++ b/src/brew.ts @@ -13,14 +13,14 @@ const servicesGenerator = (action: string): Fig.Generator => ({ }); const repositoriesGenerator = (): Fig.Generator => ({ - script: "brew tap", + script: ["brew", "tap"], postProcess: (out) => { return out.split("\n").map((line) => ({ name: line })); }, }); const formulaeGenerator: Fig.Generator = { - script: "brew list -1", + script: ["brew", "list", "-1"], postProcess: function (out) { return out .split("\n") @@ -45,7 +45,7 @@ const outdatedformulaeGenerator: Fig.Generator = { }; const generateAllFormulae: Fig.Generator = { - script: "brew formulae", + script: ["brew", "formulae"], postProcess: function (out) { return out.split("\n").map((formula) => ({ name: formula, @@ -57,7 +57,7 @@ const generateAllFormulae: Fig.Generator = { }; const generateAllCasks: Fig.Generator = { - script: "brew casks", + script: ["brew", "casks"], postProcess: function (out) { return out.split("\n").map((cask) => ({ name: cask, @@ -1274,7 +1274,7 @@ const completionSpec: Fig.Spec = { isVariadic: true, generators: { - script: "brew list -1 --cask", + script: ["brew","list","-1","--cask"], postProcess: function (out) { return out.split("\n").map((formula) => { return { diff --git a/src/bun.ts b/src/bun.ts index c43840a79552..0069180a1d62 100644 --- a/src/bun.ts +++ b/src/bun.ts @@ -303,8 +303,21 @@ const spec: Fig.Spec = { ], generators: [ { template: "folders" }, - { script: "command ls -1 ~/.bun-create", splitOn: "\n" }, - { script: "command ls -1 .bun-create", splitOn: "\n" }, + { + custom: async (_, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "ls", + args: [ + "-1", + `${context.environmentVariables["HOME"]}/.bun-create`, + ], + }); + return stdout.split("\n").map((name) => ({ + name, + })); + }, + }, + { script: ["command", "ls", "-1", ".bun-create"], splitOn: "\n" }, createCLIsGenerator, ], loadSpec: async (token) => ({ diff --git a/src/bundle.ts b/src/bundle.ts index e97e8fab6c98..3a00814243cb 100644 --- a/src/bundle.ts +++ b/src/bundle.ts @@ -1,5 +1,5 @@ const gemfileGemsGenerator: Fig.Generator = { - script: "bundle list --name-only", + script: ["bundle", "list", "--name-only"], postProcess: (out) => { return out.split("\n").map((gem) => { return { diff --git a/src/capacitor.ts b/src/capacitor.ts index 31fd5fd88501..eb0699a4555a 100644 --- a/src/capacitor.ts +++ b/src/capacitor.ts @@ -17,11 +17,12 @@ const targetGenerator: Fig.Generator = { return []; } - const out = await executeShellCommand( - `npx capacitor run ${platform} --list` - ); + const { stdout } = await executeShellCommand({ + command: "npx", + args: ["capacitor", "run", platform, "--list"], + }); - return out + return stdout .trim() .split("\n") .slice(2) // Ignore output header lines diff --git a/src/cargo.ts b/src/cargo.ts index dc05fce91274..9d7dcb2ecf6d 100644 --- a/src/cargo.ts +++ b/src/cargo.ts @@ -126,7 +126,7 @@ const rootPackageOrLocal = (manifest: Metadata) => { }; const packageGenerator: Fig.Generator = { - script: "cargo metadata --format-version 1 --no-deps", + script: ["cargo", "metadata", "--format-version", "1", "--no-deps"], postProcess: (data) => { const manifest: Metadata = JSON.parse(data); return manifest.packages.map((pkg) => { @@ -142,7 +142,7 @@ const packageGenerator: Fig.Generator = { }; const directDependencyGenerator: Fig.Generator = { - script: "cargo metadata --format-version 1", + script: ["cargo", "metadata", "--format-version", "1"], postProcess: (data: string) => { const manifest: Metadata = JSON.parse(data); const packages = rootPackageOrLocal(manifest); @@ -160,9 +160,10 @@ const targetGenerator: ({ kind }: { kind?: TargetKind }) => Fig.Generator = ({ kind, }) => ({ custom: async (_, executeShellCommand, context) => { - const out = await executeShellCommand( - "cargo metadata --format-version 1 --no-deps" - ); + const out = await executeShellCommand({ + command: "cargo", + args: ["metadata", "--format-version", "1", "--no-deps"], + }); const manifest: Metadata = JSON.parse(out); const packages = rootPackageOrLocal(manifest); @@ -184,7 +185,7 @@ const targetGenerator: ({ kind }: { kind?: TargetKind }) => Fig.Generator = ({ }); const dependencyGenerator: Fig.Generator = { - script: "cargo metadata --format-version 1", + script: ["cargo", "metadata", "--format-version", "1"], postProcess: (data: string) => { const metadata: Metadata = JSON.parse(data); return metadata.packages.map((pkg) => ({ @@ -195,7 +196,7 @@ const dependencyGenerator: Fig.Generator = { }; const featuresGenerator: Fig.Generator = { - script: "cargo read-manifest", + script: ["cargo", "read-manifest"], postProcess: (data: string) => { const manifest = JSON.parse(data); return Object.keys(manifest.features || {}).map((name) => ({ @@ -243,10 +244,11 @@ const searchGenerator: Fig.Generator = { if (lastToken.includes("@") && !lastToken.startsWith("@")) { const [crate, _version] = lastToken.split("@"); const query = encodeURIComponent(crate); - const out = await executeShellCommand( - `curl -sfL 'https://crates.io/api/v1/crates/${query}/versions'` - ); - const json: VersionSearchResults = JSON.parse(out); + const { stdout } = await executeShellCommand({ + command: "curl", + args: ["-sfL", `https://crates.io/api/v1/crates/${query}/versions`], + }); + const json: VersionSearchResults = JSON.parse(stdout); return json.versions.map((version) => ({ name: `${crate}@${version.num}`, @@ -258,14 +260,22 @@ const searchGenerator: Fig.Generator = { })); } else if (lastToken.length > 0) { const query = encodeURIComponent(lastToken); - const [remoteOut, localOut] = await Promise.all([ - executeShellCommand( - `curl -sfL 'https://crates.io/api/v1/crates?q=${query}&per_page=60'` - ), - executeShellCommand(`cargo metadata --format-version 1 --no-deps`), - ]); + const [{ stdout: remoteStdout }, { stdout: localStdout }] = + await Promise.all([ + executeShellCommand({ + command: "curl", + args: [ + "-sfL", + `https://crates.io/api/v1/crates?q=${query}&per_page=60`, + ], + }), + executeShellCommand({ + command: "cargo", + args: ["metadata", "--format-version", "1", "--no-deps"], + }), + ]); - const remoteJson: CrateSearchResults = JSON.parse(remoteOut); + const remoteJson: CrateSearchResults = JSON.parse(remoteStdout); const remoteSuggustions: Fig.Suggestion[] = remoteJson.crates .sort((a, b) => b.recent_downloads - a.recent_downloads) .map((crate) => ({ @@ -278,8 +288,8 @@ const searchGenerator: Fig.Generator = { })); let localSuggestions: Fig.Suggestion[] = []; - if (localOut.trim().length > 0) { - const localJson: Metadata = JSON.parse(localOut); + if (localStdout.trim().length > 0) { + const localJson: Metadata = JSON.parse(localStdout); localSuggestions = localJson.packages .filter((pkg) => !pkg.source) .map((pkg) => ({ @@ -308,7 +318,7 @@ const searchGenerator: Fig.Generator = { }; const tripleGenerator: Fig.Generator = { - script: "rustc --print target-list", + script: ["rustc","--print","target-list"], postProcess: (data: string) => { return data .split("\n") @@ -5785,12 +5795,17 @@ const completionSpec: (toolchain?: boolean) => Fig.Spec = ( }, ], generateSpec: async (_tokens, executeShellCommand) => { - const [toolchainOutput, listOutput] = await Promise.all([ - executeShellCommand("rustup toolchain list"), - executeShellCommand("cargo --list"), - ]); + const [{ stdout: toolchainStdout }, { stdout: listOutput }] = + await Promise.all([ + executeShellCommand({ + command: "rustup", + args: ["toolchain", "list"], + }), + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + executeShellCommand({ command: "cargo", args: ["--list"] }), + ]); - const toolchains: Fig.Option[] = toolchainOutput + const toolchains: Fig.Option[] = toolchainStdout .split("\n") .map((toolchain) => { return { diff --git a/src/cf.ts b/src/cf.ts index 648d3e93f086..ae22e674520e 100644 --- a/src/cf.ts +++ b/src/cf.ts @@ -12,12 +12,12 @@ const generateAppNames: Fig.Generator = { }; const generateOrgs: Fig.Generator = { - script: `cf orgs`, + script: ["cf", "orgs"], postProcess: postProcessCfList("Org", 3), }; const generateSpaces: Fig.Generator = { - script: `cf spaces`, + script: ["cf", "spaces"], postProcess: postProcessCfList("Space", 3), }; diff --git a/src/checkov.ts b/src/checkov.ts index d56c0189c553..c9e28d66b749 100644 --- a/src/checkov.ts +++ b/src/checkov.ts @@ -1,5 +1,5 @@ const branches: Fig.Generator = { - script: "git branch --no-color", + script: ["git", "branch", "--no-color"], postProcess: (output) => { if (output.startsWith("fatal:")) { return []; diff --git a/src/chsh.ts b/src/chsh.ts index f67870193e92..ca52ef1a91be 100644 --- a/src/chsh.ts +++ b/src/chsh.ts @@ -1,6 +1,6 @@ // TODO: this does not work on macos const shells: Fig.Generator = { - script: "chsh -l", + script: ["chsh", "-l"], postProcess: (output) => { if (output.startsWith("fatal:")) { return []; diff --git a/src/composer.ts b/src/composer.ts index 9b89eaefa316..660efdfd3571 100644 --- a/src/composer.ts +++ b/src/composer.ts @@ -40,9 +40,15 @@ const PACKAGE_REGEXP = new RegExp("^.*/.*$"); const searchGenerator: Fig.Generator = { script: function (context) { - if (context[context.length - 1] === "") return ""; + if (context[context.length - 1] === "") return undefined; const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://packagist.org/search.json?q=${searchTerm}&per_page=20"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://packagist.org/search.json?q=${searchTerm}&per_page=20`, + ]; }, postProcess: function (out) { try { @@ -62,7 +68,7 @@ const searchGenerator: Fig.Generator = { // generate package list from composer.json file const packagesGenerator: Fig.Generator = { - script: "cat composer.json", + script: ["cat", "composer.json"], postProcess: function (out) { if (out.trim() == "") { return []; @@ -97,13 +103,22 @@ const completionSpec: Fig.Spec = { name: "composer", description: "Composer Command", generateSpec: async (tokens, executeShellCommand) => { - const jsonList = await executeShellCommand("composer list --format=json"); - const symfonyLock = await executeShellCommand(`file symfony.lock`); + const [jsonList, symfonyLock] = await Promise.all([ + executeShellCommand({ + command: "composer", + args: ["list", "--format=json"], + }), + executeShellCommand({ + command: "ls", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["symfony.lock"], + }), + ]); const subcommands: Fig.Subcommand[] = []; try { - const data: ComposerListOutput = JSON.parse(jsonList); + const data: ComposerListOutput = JSON.parse(jsonList.stdout); const packagesGeneratorTriggersCommands = ["update", "remove"]; for (const command of data.commands) { @@ -204,9 +219,7 @@ const completionSpec: Fig.Spec = { }, ]; - const symfonyLockExists = !symfonyLock.endsWith( - "(No such file or directory)" - ); + const symfonyLockExists = symfonyLock.status === 0; if (symfonyLockExists) { subcommands.push({ name: ["recipes", "symfony:recipes"], diff --git a/src/conda.ts b/src/conda.ts index d20bc9c499f4..622fc1efa2ca 100644 --- a/src/conda.ts +++ b/src/conda.ts @@ -1,5 +1,5 @@ const getInstalledPackages: Fig.Generator = { - script: "conda list", + script: ["conda", "list"], postProcess: function (out) { const lines = out.split("\n"); const installedPackages = []; @@ -14,7 +14,7 @@ const getInstalledPackages: Fig.Generator = { }; // const getAllCondaPackages: Fig.Generator = { -// //script: "conda search -q", +// //script: ["conda", "search", "-q"], // script: function (context) { // if (context[context.length - 1] === "") return ""; // const searchTerm = context[context.length - 1]; @@ -35,7 +35,7 @@ const getInstalledPackages: Fig.Generator = { // }; const getCondaEnvironments: Fig.Generator = { - script: "conda env list", + script: ["conda", "env", "list"], scriptTimeout: 10000, cache: { ttl: 10000, @@ -56,7 +56,7 @@ const getCondaEnvironments: Fig.Generator = { }; const getCondaConfigs: Fig.Generator = { - script: "conda config --show", + script: ["conda","config","--show"], postProcess: function (out) { const lines = out.split("\n"); const configs: Fig.Suggestion[] = []; diff --git a/src/copilot.ts b/src/copilot.ts index bd4e7af78b73..36c980ae7bde 100644 --- a/src/copilot.ts +++ b/src/copilot.ts @@ -1,7 +1,7 @@ import YAML from "yaml"; const applicationName: Fig.Generator = { - script: "cat copilot/.workspace", + script: ["cat","copilot/.workspace"], // TODO: I feel like there's a better way to do this. // There's only ever expected to be one `application` key. postProcess: (output) => { diff --git a/src/cordova.ts b/src/cordova.ts index aac5d9dd4097..2110d3e7efc8 100644 --- a/src/cordova.ts +++ b/src/cordova.ts @@ -24,7 +24,7 @@ const commonOptions: Fig.Option[] = [ ]; const platformGenerator: Fig.Generator = { - script: "cat package.json", + script: ["cat","package.json"], postProcess: function (out: string) { const suggestions = []; try { @@ -51,7 +51,7 @@ const platformGenerator: Fig.Generator = { }; const pluginGenerator: Fig.Generator = { - script: "cordova plugin list", + script: ["cordova", "plugin", "list"], postProcess: (out: string) => out.split("\n").map((pluginName) => ({ name: pluginName, diff --git a/src/dcli.ts b/src/dcli.ts index 9ddd566c303b..c172b67476ec 100644 --- a/src/dcli.ts +++ b/src/dcli.ts @@ -31,7 +31,7 @@ const helpCommand: Fig.Subcommand = { }; const deviceGenerator: Fig.Generator = { - script: "dcli devices list --json", + script: ["dcli", "devices", "list", "--json"], postProcess: function (out) { try { const devices = JSON.parse(out) as Device[]; @@ -51,7 +51,7 @@ const deviceGenerator: Fig.Generator = { }; const teamCredentialGenerator: Fig.Generator = { - script: "dcli team credentials list --json", + script: ["dcli", "team", "credentials", "list", "--json"], postProcess: function (out) { try { const credentials = JSON.parse(out) as TeamCredential[]; diff --git a/src/defaultbrowser.ts b/src/defaultbrowser.ts index 40f5b2460db8..fa5f0aa1d2e7 100644 --- a/src/defaultbrowser.ts +++ b/src/defaultbrowser.ts @@ -1,5 +1,5 @@ const getInstalledBrowsers: Fig.Generator = { - script: "defaultbrowser", + script: ["defaultbrowser"], postProcess: function (out) { return out.split("\n").map((line) => { /* We ignore the already set browser */ diff --git a/src/defaults.ts b/src/defaults.ts index 5354a31c2ff7..4350993c4ece 100644 --- a/src/defaults.ts +++ b/src/defaults.ts @@ -1,7 +1,7 @@ const domain: Fig.Arg = { name: "domain", generators: { - script: "defaults domains", + script: ["defaults", "domains"], postProcess: function (out) { return out.split(",").map((domain) => { return { diff --git a/src/degit.ts b/src/degit.ts index d3fa05c87f8c..116d32d527c0 100644 --- a/src/degit.ts +++ b/src/degit.ts @@ -120,10 +120,11 @@ const completionSpec: Fig.Spec = { const lastToken = tokens[tokens.length - 1]; if (lastToken.includes(":")) return []; const username = lastToken.slice(0, lastToken.indexOf("/")); - const json = await executeShellCommand( - `curl -sL 'https://api.github.com/users/${username}/repos'` - ); - const repos = JSON.parse(json) as Repository[]; + const { stdout } = await executeShellCommand({ + command: "curl", + args: ["-sL", `https://api.github.com/users/${username}/repos`], + }); + const repos = JSON.parse(stdout) as Repository[]; return repos.map((repo) => ({ name: repo.full_name, description: repo.description ?? "Repository", diff --git a/src/deno/generators.ts b/src/deno/generators.ts index db9b1855483f..76de2774d02d 100644 --- a/src/deno/generators.ts +++ b/src/deno/generators.ts @@ -244,7 +244,7 @@ type DenoLintRulesJSON = { }[]; export const generateLintRules: Fig.Generator = { - script: "deno lint --rules --json", + script: ["deno", "lint", "--rules", "--json"], getQueryTerm: ",", cache: { ttl: 1000 * 60 * 60 * 24 }, postProcess: (out) => { diff --git a/src/deta.ts b/src/deta.ts index c0803eff4652..e86776d8c989 100644 --- a/src/deta.ts +++ b/src/deta.ts @@ -7,17 +7,15 @@ // Fig generator for runtime options. Manually coded from // https://docs.deta.sh/docs/cli/commands#deta-new -const runtimes: Fig.Generator = { - script: "echo node12, node14, python3.7, python3.9", - postProcess: (output) => { - return output.split(",").map((runtime) => { - return { - name: runtime, - description: "Runtime", - }; - }); - }, -}; +const runtimes: Fig.Suggestion[] = [ + "node12", + "node14", + "python3.7", + "python3.9", +].map((runtime) => ({ + name: runtime, + description: "Runtime", +})); const completionSpec: Fig.Spec = { name: "deta", @@ -115,7 +113,7 @@ const completionSpec: Fig.Spec = { args: { name: "runtime", description: "The selected runtime", - generators: runtimes, + suggestions: runtimes, }, }, ], @@ -325,7 +323,7 @@ const completionSpec: Fig.Spec = { args: { name: "runtime", description: "New runtime for the micro", - generators: runtimes, + suggestions: runtimes, }, }, { diff --git a/src/docker.ts b/src/docker.ts index 66a89a2dc4cf..99dff039066a 100644 --- a/src/docker.ts +++ b/src/docker.ts @@ -26,19 +26,26 @@ const sharedPostProcess: Fig.Generator["postProcess"] = (out) => { const dockerGenerators: Record = { runningDockerContainers: { - script: `docker ps --format '{{ json . }}'`, + script: ["docker", "ps", "--format", "{{ json . }}"], postProcess: postProcessDockerPs, }, allDockerContainers: { - script: `docker ps -a --format '{{ json . }}'`, + script: ["docker", "ps", "-a", "--format", "{{ json . }}"], postProcess: postProcessDockerPs, }, pausedDockerContainers: { - script: `docker ps --filter status=paused --format '{{ json . }}'`, + script: [ + "docker", + "ps", + "--filter", + "status=paused", + "--format", + "{{ json . }}", + ], postProcess: postProcessDockerPs, }, allLocalImages: { - script: `docker image ls --format '{{ json . }}'`, + script: ["docker", "image", "ls", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -51,7 +58,7 @@ const dockerGenerators: Record = { }, }, allLocalImagesWithRepository: { - script: `docker image ls --format '{{ json . }}'`, + script: ["docker", "image", "ls", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -65,9 +72,9 @@ const dockerGenerators: Record = { }, dockerHubSearch: { script: function (context) { - if (context[context.length - 1] === "") return ""; + if (context[context.length - 1] === "") return undefined; const searchTerm = context[context.length - 1]; - return `docker search ${searchTerm} --format '{{ json . }}'`; + return ["docker", "search", searchTerm, "--format", "{{ json . }}"]; }, postProcess: function (out) { return out @@ -83,7 +90,7 @@ const dockerGenerators: Record = { }, }, allDockerContexts: { - script: `docker context list --format '{{ json . }}'`, + script: ["docker", "context", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -96,11 +103,11 @@ const dockerGenerators: Record = { }, }, listDockerNetworks: { - script: `docker network list --format '{{ json . }}'`, + script: ["docker", "network", "list", "--format", "{{ json . }}"], postProcess: sharedPostProcess, }, listDockerSwarmNodes: { - script: `docker node list --format '{{ json . }}'`, + script: ["docker", "node", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -114,15 +121,15 @@ const dockerGenerators: Record = { }, }, listDockerPlugins: { - script: `docker plugin list --format '{{ json . }}'`, + script: ["docker", "plugin", "list", "--format", "{{ json . }}"], postProcess: sharedPostProcess, }, listDockerSecrets: { - script: `docker secret list --format '{{ json . }}'`, + script: ["docker", "secret", "list", "--format", "{{ json . }}"], postProcess: sharedPostProcess, }, listDockerServices: { - script: `docker service list --format '{{ json . }}'`, + script: ["docker", "service", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -135,7 +142,7 @@ const dockerGenerators: Record = { }, }, listDockerServicesReplicas: { - script: `docker service list --format '{{ json . }}'`, + script: ["docker", "service", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -148,7 +155,7 @@ const dockerGenerators: Record = { }, }, listDockerStacks: { - script: `docker stack list --format '{{ json . }}'`, + script: ["docker", "stack", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -160,7 +167,7 @@ const dockerGenerators: Record = { }, }, listDockerVolumes: { - script: `docker volume list --format '{{ json . }}'`, + script: ["docker", "volume", "list", "--format", "{{ json . }}"], postProcess: function (out) { return out .split("\n") @@ -346,10 +353,10 @@ const sharedCommands: Record = { fileFlagIndex = context.indexOf("--file"); dockerfilePath = context[fileFlagIndex + 1]; } else { - dockerfilePath = "$PWD/Dockerfile"; + dockerfilePath = "Dockerfile"; } - return `\\grep -iE 'FROM.*AS' "${dockerfilePath}"`; + return ["grep", "-iE", "FROM.*AS", dockerfilePath]; }, postProcess: function (out) { // This just searches the Dockerfile for the alias name after AS, @@ -1978,8 +1985,12 @@ default-cgroupns-mode option on the daemon (default)`, name: "image", description: "The Docker image to use", generators: { - script: - "docker images --format '{{.Repository}} {{.Size}} {{.Tag}} {{.ID}}'", + script: [ + "docker", + "images", + "--format", + "{{.Repository}} {{.Size}} {{.Tag}} {{.ID}}", + ], postProcess: function (out) { return out.split("\n").map((image) => { const [repo, size, tag, id] = image.split(" "); @@ -2599,7 +2610,7 @@ const completionSpec: Fig.Spec = { name: "Name or ID", generators: [ { - script: `docker ps -a --format '{{ json . }}'`, + script: ["docker", "ps", "-a", "--format", "{{ json . }}"], postProcess: function (out) { const allLines = out.split("\n").map((line) => JSON.parse(line)); return allLines.map((i) => ({ @@ -2609,7 +2620,7 @@ const completionSpec: Fig.Spec = { }, }, { - script: `docker images -a --format '{{ json . }}'`, + script: ["docker", "images", "-a", "--format", "{{ json . }}"], postProcess: function (out) { const allLines = out.split("\n").map((line) => JSON.parse(line)); return allLines.map((i) => { @@ -2631,7 +2642,7 @@ const completionSpec: Fig.Spec = { }, }, { - script: `docker volume ls --format '{{ json . }}'`, + script: ["docker", "volume", "ls", "--format", "{{ json . }}"], postProcess: function (out) { const allLines = out.split("\n").map((line) => JSON.parse(line)); return allLines.map((i) => ({ diff --git a/src/doppler.ts b/src/doppler.ts index 80a5cc4001bc..fa5dd6a90c11 100644 --- a/src/doppler.ts +++ b/src/doppler.ts @@ -4,7 +4,7 @@ const enviornmentsGenerator: Fig.Generator = { cacheKey: "enviornments", cacheByDirectory: true, }, - script: "doppler environments --json", + script: ["doppler", "environments", "--json"], postProcess: (out) => { try { const obj = JSON.parse(out); @@ -24,7 +24,7 @@ const configGenerators: Fig.Generator = { cacheKey: "configs", cacheByDirectory: true, }, - script: "doppler configs --json", + script: ["doppler", "configs", "--json"], postProcess: (out) => { try { const obj = JSON.parse(out); @@ -44,7 +44,7 @@ const secretsGenerator: Fig.Generator = { cacheKey: "secrets", cacheByDirectory: true, }, - script: "doppler secrets --only-names --json", + script: ["doppler", "secrets", "--only-names", "--json"], postProcess: (out) => { try { const obj = JSON.parse(out); @@ -64,7 +64,7 @@ const projectsGenerator: Fig.Generator = { cacheKey: "projects", cacheByDirectory: true, }, - script: "doppler projects --json", + script: ["doppler","projects","--json"], postProcess: (out) => { try { const obj = JSON.parse(out); diff --git a/src/dotnet.ts b/src/dotnet.ts index c41ec1a37cd3..f519cc3e5692 100644 --- a/src/dotnet.ts +++ b/src/dotnet.ts @@ -211,9 +211,12 @@ const completionSpec: Fig.Spec = { const argRegex = /(([a-zA-Z \.\[\]#,/][^ ]{1,})+)/g; const subcommands: Fig.Subcommand[] = []; - const toolList = await executeShellCommand("dotnet tool list --global"); + const { stdout } = await executeShellCommand({ + command: "dotnet", + args: ["tool", "list", "--global"], + }); - const lines = toolList.split("\n").slice(2); + const lines = stdout.split("\n").slice(2); for (const line of lines) { const [_, __, command] = line diff --git a/src/dotnet/dotnet-add.ts b/src/dotnet/dotnet-add.ts index 72b7e141e288..0c9d8b8691f7 100644 --- a/src/dotnet/dotnet-add.ts +++ b/src/dotnet/dotnet-add.ts @@ -9,7 +9,13 @@ type PackageSearchResultData = { const packageSearchGenerator: Fig.Generator = { script(context) { const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://azuresearch-usnc.nuget.org/query?packageType=Dependency&q=${searchTerm}"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://azuresearch-usnc.nuget.org/query?packageType=Dependency&q=${searchTerm}`, + ]; }, postProcess(out) { const searchResults: PackageSearchResultData[] = JSON.parse(out).data; @@ -30,7 +36,13 @@ const versionSearchGenerator: Fig.Generator = { const idx = command.findIndex((ctx) => ctx === "package"); const searchTerm = command[idx + 1].toLowerCase(); - return `curl -s -H "Accept: application/json" "https://api.nuget.org/v3-flatcontainer/${searchTerm}/index.json"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://api.nuget.org/v3-flatcontainer/${searchTerm}/index.json`, + ]; }, postProcess(out) { const searchResults: string[] = JSON.parse(out).versions; diff --git a/src/dotnet/dotnet-new.ts b/src/dotnet/dotnet-new.ts index ff15f9659f6f..c5e8d3e1203b 100644 --- a/src/dotnet/dotnet-new.ts +++ b/src/dotnet/dotnet-new.ts @@ -7,7 +7,13 @@ type SearchResultData = { const searchGenerator: Fig.Generator = { script(context) { const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://azuresearch-usnc.nuget.org/query?packageType=Template&q=${searchTerm}"`; + return [ + "curl", + "-sfL", + "-H", + "Accept: application/json", + `https://azuresearch-usnc.nuget.org/query?packageType=Template&q=${searchTerm}`, + ]; }, postProcess(out) { const searchResults: SearchResultData[] = JSON.parse(out).data; @@ -182,7 +188,7 @@ const completionSpec: Fig.Spec = { description: "The template to instantiate when the command is invoked. Each template might have specific options you can pass", generators: { - script: "dotnet new --list", + script: ["dotnet", "new", "--list"], postProcess(out) { const lines = out.split("\n").slice(4); diff --git a/src/dotnet/dotnet-run.ts b/src/dotnet/dotnet-run.ts index 185093ee0dc9..c8cc4e23d06b 100644 --- a/src/dotnet/dotnet-run.ts +++ b/src/dotnet/dotnet-run.ts @@ -58,7 +58,7 @@ const completionSpec: Fig.Spec = { name: "name", suggestions: ["Development", "Staging", "Production"], generators: { - script: "cat Properties/launchSettings.json", + script: ["cat","Properties/launchSettings.json"], postProcess(out) { const profiles: LaunchProfiles = JSON.parse(out).profiles; diff --git a/src/dotnet/dotnet-tool.ts b/src/dotnet/dotnet-tool.ts index a6c9c2e50707..11306b390eb2 100644 --- a/src/dotnet/dotnet-tool.ts +++ b/src/dotnet/dotnet-tool.ts @@ -9,7 +9,13 @@ type SearchResultData = { const packageGenerator: Fig.Generator = { script(context) { const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://azuresearch-usnc.nuget.org/query?packageType=DotnetTool&q=${searchTerm}"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://azuresearch-usnc.nuget.org/query?packageType=DotnetTool&q=${searchTerm}`, + ]; }, postProcess(out) { const searchResults: SearchResultData[] = JSON.parse(out).data; @@ -31,7 +37,13 @@ const versionSearchGenerator: Fig.Generator = { const idx = command.findIndex((ctx) => commands.includes(ctx)); const searchTerm = command[idx + 1].toLowerCase(); - return `curl -s -H "Accept: application/json" "https://api.nuget.org/v3-flatcontainer/${searchTerm}/index.json"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://api.nuget.org/v3-flatcontainer/${searchTerm}/index.json`, + ]; }, postProcess(out) { const searchResults: string[] = JSON.parse(out).versions; diff --git a/src/dscacheutil.ts b/src/dscacheutil.ts index 5e73d575719e..36728e695f3f 100644 --- a/src/dscacheutil.ts +++ b/src/dscacheutil.ts @@ -52,7 +52,12 @@ const dscacheutilGenerators: Record = { const attributeKey = tokens[tokens.length - 2]; const pp = postProcessQuery({ attributeKey }); return pp( - await executeShellCommand(`dscacheutil -q ${category}`), + ( + await executeShellCommand({ + command: "dscacheutil", + args: ["-q", category], + }) + ).stdout, tokens ); }, diff --git a/src/dtm.ts b/src/dtm.ts index 846b0fe8827c..4066bb62f6be 100644 --- a/src/dtm.ts +++ b/src/dtm.ts @@ -1,7 +1,7 @@ import { filepaths } from "@fig/autocomplete-generators"; const dtmGenerators: Record = { plugins: { - script: "dtm list plugins", + script: ["dtm", "list", "plugins"], postProcess: (output) => { if (output.startsWith("fatal:")) { return []; diff --git a/src/eb.ts b/src/eb.ts index 88aa0e84a206..5ec3d2c96e63 100644 --- a/src/eb.ts +++ b/src/eb.ts @@ -1,5 +1,5 @@ const generateNames: Fig.Generator = { - script: "eb list", + script: ["eb", "list"], postProcess: (str) => { const lines: string[] = str .trim() diff --git a/src/elm-format.ts b/src/elm-format.ts index 81a6e01e345a..128780524ba7 100644 --- a/src/elm-format.ts +++ b/src/elm-format.ts @@ -1,16 +1,11 @@ -const supportedElmVersions: Fig.Generator = { - script: "echo", - postProcess: () => { - return [ - { - name: "0.19", - }, - { - name: "0.18", - }, - ]; +const supportedElmVersions = [ + { + name: "0.19", }, -}; + { + name: "0.18", + }, +]; /** * Based on [elm-format](https://github.com/avh4/elm-format), version 0.8.5. A cli tool for formatting Elm code. @@ -51,7 +46,7 @@ const completionSpec: Fig.Spec = { args: { name: "VERSION", description: "Valid values: 0.18, 0.19. Default: auto", - generators: supportedElmVersions, + suggestions: supportedElmVersions, isOptional: true, }, }, diff --git a/src/elm-json.ts b/src/elm-json.ts index 910c029407c7..a6f5fb944458 100644 --- a/src/elm-json.ts +++ b/src/elm-json.ts @@ -1,8 +1,13 @@ import { filepaths } from "@fig/autocomplete-generators"; const packageList: Fig.Generator = { - script: - "curl -sH 'accept-encoding: gzip' --compressed https://package.elm-lang.org/search.json", + script: [ + "curl", + "-sH", + "accept-encoding: gzip", + "--compressed", + "https://package.elm-lang.org/search.json", + ], cache: { ttl: 1000 * 24 * 60 * 60 * 3, // 3 days }, diff --git a/src/elm-review.ts b/src/elm-review.ts index 758d12c19829..f34e7966d0cb 100644 --- a/src/elm-review.ts +++ b/src/elm-review.ts @@ -1,21 +1,16 @@ /** * The report type is either `json` or `ndjson` */ -const reportType: Fig.Generator = { - script: "echo", - postProcess: () => { - return [ - { - name: "json", - description: "Prints a single JSON object", - }, - { - name: "ndjson", - description: "Print one JSON object per error each on a new line", - }, - ]; +const reportType = [ + { + name: "json", + description: "Prints a single JSON object", }, -}; + { + name: "ndjson", + description: "Print one JSON object per error each on a new line", + }, +]; /** * Based on [elm-review](https://github.com/jfmengels/node-elm-review), version 2.9.1. Cli tool for reviewing Elm code. @@ -198,7 +193,7 @@ const completionSpec: Fig.Spec = { description: "Error reports will be in JSON format", args: { name: "json or ndjson", - generators: reportType, + suggestions: reportType, }, }, { diff --git a/src/elm.ts b/src/elm.ts index 27f802f8981c..6903637d8c1a 100644 --- a/src/elm.ts +++ b/src/elm.ts @@ -1,6 +1,11 @@ const packageList: Fig.Generator = { - script: - "curl -sH 'accept-encoding: gzip' --compressed https://package.elm-lang.org/search.json", + script: [ + "curl", + "-sH", + "accept-encoding: gzip", + "--compressed", + "https://package.elm-lang.org/search.json", + ], cache: { ttl: 1000 * 24 * 60 * 60 * 3, // 3 days }, diff --git a/src/env.ts b/src/env.ts index 65ac5ccd66e5..de01be77f23d 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,11 +1,12 @@ const enviromentVariables: Fig.Generator = { - script: "env | cut -d '=' -f 1", - postProcess: (out) => { - return out.split("\n").map((envVar) => ({ - name: envVar, - description: "Environment variable", - icon: "🌎", - })); + custom: async (_tokens, _executeCommand, generatorContext) => { + return Object.values(generatorContext.environmentVariables).map( + (envVar) => ({ + name: envVar, + description: "Environment variable", + icon: "🌎", + }) + ); }, }; diff --git a/src/envchain.ts b/src/envchain.ts index 4ad6e057892a..bdba19294bc8 100644 --- a/src/envchain.ts +++ b/src/envchain.ts @@ -1,5 +1,5 @@ const namespaces: Fig.Generator = { - script: "envchain --list", + script: ["envchain", "--list"], postProcess: (output) => { return Array.from(new Set(output.split("\n"))).map((namespace) => { return { diff --git a/src/expo-cli.ts b/src/expo-cli.ts index 3b5402c68d03..a5a2ee7572fd 100644 --- a/src/expo-cli.ts +++ b/src/expo-cli.ts @@ -5,9 +5,15 @@ const _gen: Record = { npm: { script(context) { - if (context[context.length - 1] === "") return ""; + if (context[context.length - 1] === "") return undefined; const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://api.npms.io/v2/search?q=${searchTerm}&size=20"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://api.npms.io/v2/search?q=${searchTerm}&size=20`, + ]; }, postProcess(script: string) { try { @@ -38,7 +44,7 @@ const _gen: Record = { JSON.parse(script).project.schemes.map((name) => ({ name })), }, "xcode-devices": { - script: "xcrun xctrace list devices", + script: ["xcrun", "xctrace", "list", "devices"], postProcess: (script: string) => script .split("\n") @@ -53,7 +59,7 @@ const _gen: Record = { })), }, "max-workers": { - script: "sysctl -n hw.ncpu", + script: ["sysctl", "-n", "hw.ncpu"], postProcess: (script: string) => { const count = Number(script); return Array.from({ length: count }, (_, i) => { diff --git a/src/expo.ts b/src/expo.ts index 270890b22979..06e87df4dd03 100644 --- a/src/expo.ts +++ b/src/expo.ts @@ -5,9 +5,15 @@ const _gen: Record = { npm: { script(context) { - if (context[context.length - 1] === "") return ""; + if (context[context.length - 1] === "") return undefined; const searchTerm = context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://api.npms.io/v2/search?q=${searchTerm}&size=20"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://api.npms.io/v2/search?q=${searchTerm}&size=20`, + ]; }, postProcess(script: string) { try { @@ -28,17 +34,17 @@ const _gen: Record = { }, }, "xcode-configuration": { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["xcodebuild", "-project", "ios/*.xcodeproj", "-list", "-json"], postProcess: (script: string) => JSON.parse(script).project.configurations.map((name) => ({ name })), }, "xcode-scheme": { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["xcodebuild", "-project", "ios/*.xcodeproj", "-list", "-json"], postProcess: (script: string) => JSON.parse(script).project.schemes.map((name) => ({ name })), }, "xcode-devices": { - script: "xcrun xctrace list devices", + script: ["xcrun", "xctrace", "list", "devices"], postProcess: (script: string) => script .split("\n") @@ -53,7 +59,7 @@ const _gen: Record = { })), }, "max-workers": { - script: "sysctl -n hw.ncpu", + script: ["sysctl", "-n", "hw.ncpu"], postProcess: (script: string) => { const count = Number(script); return Array.from({ length: count }, (_, i) => { diff --git a/src/expressots.ts b/src/expressots.ts index f4069e3b2f56..7c972f899051 100644 --- a/src/expressots.ts +++ b/src/expressots.ts @@ -17,7 +17,7 @@ const completionSpec: Fig.Spec = { name: "template", description: "Choose a template", generators: { - script: "expressots templates", + script: ["expressots", "templates"], postProcess: function (out) { return [ { @@ -40,7 +40,7 @@ const completionSpec: Fig.Spec = { name: "package-manager", description: "Choose a package manager", generators: { - script: "expressots package manager", + script: ["expressots", "package", "manager"], postProcess: function (out) { return [ { diff --git a/src/ffmpeg.ts b/src/ffmpeg.ts index a6df9c56817e..00d5f996d94d 100644 --- a/src/ffmpeg.ts +++ b/src/ffmpeg.ts @@ -122,7 +122,7 @@ const completionSpec: Fig.Spec = { args: { name: "device", generators: { - script: "ffmpeg -devices", + script: ["ffmpeg", "-devices"], postProcess: (out) => { return out .split("\n") @@ -143,7 +143,7 @@ const completionSpec: Fig.Spec = { args: { name: "device", generators: { - script: "ffmpeg -devices", + script: ["ffmpeg", "-devices"], postProcess: (out) => { return out .split("\n") @@ -387,7 +387,7 @@ const completionSpec: Fig.Spec = { args: { name: "device", generators: { - script: "ffmpeg -devices", + script: ["ffmpeg", "-devices"], postProcess: (out) => { return out .split("\n") @@ -413,7 +413,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg","-codecs"], postProcess: (out) => { return out .split("\n") @@ -434,7 +434,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") @@ -715,7 +715,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") @@ -765,7 +765,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") @@ -924,7 +924,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg","-codecs"], postProcess: (out) => { return out .split("\n") @@ -990,7 +990,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: "ffmpeg -codecs", + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") diff --git a/src/fig-teams.ts b/src/fig-teams.ts deleted file mode 100644 index 77dab05bbb1f..000000000000 --- a/src/fig-teams.ts +++ /dev/null @@ -1,8 +0,0 @@ -import fig_teams from "./fig-teams@latest"; - -const completionSpec: Fig.Spec = { - ...fig_teams, - name: "fig-teams@latest", -}; - -export default completionSpec; diff --git a/src/fig-teams@latest.ts b/src/fig-teams@latest.ts deleted file mode 100644 index 5cd7bf02754c..000000000000 --- a/src/fig-teams@latest.ts +++ /dev/null @@ -1,119 +0,0 @@ -const getTeamsGenerator: Fig.Generator = { - script: "npx -y fig-teams@latest teams ls --json", - postProcess: (output) => { - try { - const teams = JSON.parse(output) as Array; - return teams.map((name) => ({ - name, - })); - } catch (e) { - return []; - } - }, -}; - -const getUsersGenerator: Fig.Generator = { - script: (context) => { - const teamOption = context.findIndex( - (ctx) => ctx === "-t" || ctx === "--team" - ); - // try getting the value of the team option - if (teamOption !== -1 && context[teamOption + 1] !== undefined) { - const teamName = context[teamOption + 1]; - return `npx -y fig-teams@latest users get -t ${teamName} --json`; - } - return ""; - }, - postProcess: (output) => { - try { - const teams = JSON.parse(output) as Array<{ - email: string; - role: string; - }>; - return teams.map(({ email }) => ({ - name: email, - icon: "fig://icon?type=invite", - })); - } catch (e) { - return []; - } - }, -}; - -const teamOption: Fig.Option = { - name: ["-t", "--team"], - description: "Team to use", - args: { - name: "teamname", - generators: getTeamsGenerator, - }, -}; - -const jsonOption: Fig.Option = { - name: "--json", - description: "Output as JSON", -}; - -const completionSpec: Fig.Spec = { - name: "fig-teams", - description: "Fig for teams", - subcommands: [ - { - name: "teams", - subcommands: [ - { - name: "ls", - description: "Get all available teams for the current user", - options: [jsonOption], - }, - { - name: "create", - description: "Create a new team", - args: { - name: "team_name", - }, - }, - { - name: "ls:user", - description: "Get all users for a given team", - options: [teamOption, jsonOption], - }, - { - name: "add:user", - description: "Add a new user to a team", - options: [teamOption], - args: { - name: "email", - generators: getUsersGenerator, - }, - }, - { - name: "remove:user", - description: "Remove a user from a team", - options: [teamOption], - args: { - name: "email", - generators: getUsersGenerator, - }, - }, - ], - }, - { - name: "deploy", - description: - "Push your locally compiled completion specs to Fig's server based on the mapping defined in the .fig/manifest file", - }, - { - name: "manifest", - description: - "Create or update the .fig/manifest file. Use this command to link your locally created completion specs to your team", - }, - { - name: "whoami", - description: - "Get information about the current user and their associated teams", - }, - ], -}; - -export default completionSpec; diff --git a/src/fig/index.ts b/src/fig/index.ts index c1162e841c81..9eca40bcea23 100644 --- a/src/fig/index.ts +++ b/src/fig/index.ts @@ -3,7 +3,11 @@ const versionFiles = ["1.0.0", "2.0.0"]; export const getVersionCommand: Fig.GetVersionCommand = async ( executeShellCommand ) => { - const out = await executeShellCommand("fig --version"); - return out.slice(out.indexOf(" ") + 1); + const { stdout } = await executeShellCommand({ + command: "fig", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--version"], + }); + return stdout.slice(stdout.indexOf(" ") + 1); }; export default createVersionedSpec("fig", versionFiles); diff --git a/src/fig/shared.ts b/src/fig/shared.ts index 8e3c1e812f27..e1462b725c30 100644 --- a/src/fig/shared.ts +++ b/src/fig/shared.ts @@ -18,16 +18,25 @@ const graphql = async ({ exec, query, }: { - exec: Fig.ExecuteShellCommandFunction; + exec: Fig.ExecuteCommandFunction; query: string; }) => { - const response = await exec( - `fig _ request --route '/graphql' --method POST --body '{ "query": "${query - .split(/\s+/) - .join(" ")}" }'` - ); + const { stdout } = await exec({ + command: "fig", + args: [ + "_", + "request", + "--route", + "/graphql", + "--method", + "--body", + JSON.stringify({ + query, + }), + ], + }); - return JSON.parse(response).data; + return JSON.parse(stdout).data; }; const devCompletionsFolderGenerator: Fig.Generator = { @@ -46,7 +55,7 @@ const devCompletionsFolderGenerator: Fig.Generator = { }; const disableForCommandsGenerator: Fig.Generator = { - script: "fig settings autocomplete.disableForCommands", + script: ["fig", "settings", "autocomplete.disableForCommands"], postProcess: (out) => { const existing = out.split("\n").filter((item) => item.length > 0); @@ -78,7 +87,7 @@ const disableForCommandsGenerator: Fig.Generator = { }; export const themesGenerator: Fig.Generator = { - script: "fig theme --list", + script: ["fig", "theme", "--list"], postProcess: (output) => { const builtinThemes: Fig.Suggestion[] = [ { @@ -137,10 +146,11 @@ export const settingsSpecGenerator: Fig.Subcommand["generateSpec"] = async ( _, executeShellCommand ) => { - const text = await executeShellCommand( - "fig _ request --method GET --route '/settings/all'" - ); - const { settings, actions } = JSON.parse(text) as { + const { stdout } = await executeShellCommand({ + command: "fig", + args: ["_", "request", "--method", "GET", "--route", "/settings/all"], + }); + const { settings, actions } = JSON.parse(stdout) as { settings: Setting[]; actions: Action[]; }; @@ -195,7 +205,7 @@ export const settingsSpecGenerator: Fig.Subcommand["generateSpec"] = async ( }; export const stateGenerator: Fig.Generator = { - script: "fig internal local-state all --format json", + script: ["fig", "internal", "local-state", "all", "--format", "json"], postProcess: (out) => { const state = JSON.parse(out); return Object.keys(state).map((key) => ({ @@ -218,11 +228,14 @@ export const pluginsGenerator = (init: { strategy: "stale-while-revalidate", }, custom: async (_tokens, executeShellCommand) => { - const script = init.installed - ? "fig plugins list --format json --installed" - : "fig plugins list --format json"; - const out = await executeShellCommand(script); - const json = JSON.parse(out) as Plugin[]; + const args = init.installed + ? ["plugins", "list", "--format", "json", "--installed"] + : ["plugins", "list", "--format", "json"]; + const { stdout } = await executeShellCommand({ + command: "fig", + args, + }); + const json = JSON.parse(stdout) as Plugin[]; return json.map((plugin) => ({ name: plugin.name, icon: !plugin.icon?.startsWith("https://") ? plugin.icon : "📦", @@ -252,9 +265,20 @@ export const tokensGenerators: Fig.Generator = { teamName = tokens[teamOptionIndex + 1]; } const out = JSON.parse( - await executeShellCommand( - `fig user tokens list --team ${teamName} --format json` - ) + ( + await executeShellCommand({ + command: "fig", + args: [ + "user", + "tokens", + "list", + "--team", + teamName, + "--format", + "json", + ], + }) + ).stdout ) as { createdAt: string; description?: string; @@ -278,7 +302,7 @@ export const teamsGenerators: Fig.Generator = { cache: { strategy: "stale-while-revalidate", }, - script: "fig team --list --format json", + script: ["fig", "team", "--list", "--format", "json"], postProcess: (out) => { return ( JSON.parse(out) as { id: number; name: string; specs: string[] }[] @@ -295,7 +319,12 @@ export const membersGenerators: Fig.Generator = { custom: async (tokens, executeShellCommand) => { const teamName = tokens.at(-3); const out = JSON.parse( - await executeShellCommand(`fig team --format json ${teamName} members`) + ( + await executeShellCommand({ + command: "fig", + args: ["team", "--format", "json", teamName, "members"], + }) + ).stdout ) as { email: string; role: string }[]; return out.map((member) => { return { @@ -315,9 +344,12 @@ export const invitationsGenerators: Fig.Generator = { custom: async (tokens, executeShellCommand) => { const teamName = tokens.at(-3); const out = JSON.parse( - await executeShellCommand( - `fig team --format json ${teamName} invitations` - ) + ( + await executeShellCommand({ + command: "fig", + args: ["team", "--format", "json", teamName, "invitations"], + }) + ).stdout ) as { email: string; role: string }[]; return out.map((invitation) => { return { @@ -451,7 +483,7 @@ const scriptOptions = (script: ScriptFields) => { generators = param?.selector?.generators .filter((generator) => generator.type === "ShellScript") .map((generator) => ({ - script: generator?.shellScript?.script, + script: ["bash", "-c", generator?.shellScript?.script], splitOn: "\n", })); } @@ -732,7 +764,15 @@ export const commandLineToolSpecGenerator: Fig.Subcommand["generateSpec"] = }; export const sshHostsGenerator: Fig.Generator = { - script: "fig _ request --method GET --route /access/hosts/all", + script: [ + "fig", + "_", + "request", + "--method", + "GET", + "--route", + "/access/hosts/all", + ], cache: { strategy: "stale-while-revalidate", }, @@ -759,7 +799,12 @@ export const sshIdentityGenerator: Fig.Generator = { return []; } const hosts = JSON.parse( - await executeShellCommand(`fig ssh ${host} --get-identities`) + ( + await executeShellCommand({ + command: "fig", + args: ["ssh", host, "--get-identities"], + }) + ).stdout ) as { displayName: string; username: string }[]; return hosts.map((host) => ({ @@ -769,7 +814,7 @@ export const sshIdentityGenerator: Fig.Generator = { }; export const userGenerator: Fig.Generator = { - script: "fig user list-accounts", + script: ["fig", "user", "list-accounts"], postProcess: (out) => { if (out.startsWith("error: ")) { return []; diff --git a/src/fin.ts b/src/fin.ts index 86c411a06c74..70c96b8fcac6 100644 --- a/src/fin.ts +++ b/src/fin.ts @@ -1,5 +1,5 @@ const databases: Fig.Generator = { - script: "fin db list", + script: ["fin", "db", "list"], postProcess: (output) => { return output.split("\n").map((db) => { return { name: db.trim(), description: "Database" }; @@ -8,7 +8,7 @@ const databases: Fig.Generator = { }; const hosts: Fig.Generator = { - script: "fin hosts", + script: ["fin", "hosts"], postProcess: (output) => { return output.split("\n").map((host) => { if (host.startsWith("#")) { @@ -20,7 +20,7 @@ const hosts: Fig.Generator = { }; const aliasGenerator: Fig.Generator = { - script: "fin alias list", + script: ["fin", "alias", "list"], postProcess: (output) => { return output .split("\n") @@ -32,7 +32,7 @@ const aliasGenerator: Fig.Generator = { }; const serviceGenerator: Fig.Generator = { - script: "\\command fin docker ps --format '{{.Names}}'", + script: ["fin", "docker", "ps", "--format", "{{.Names}}"], splitOn: "\n", }; diff --git a/src/firebase.ts b/src/firebase.ts index e640e18743ea..bddab501a17c 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -1,5 +1,5 @@ const projectAliasesGenerator: Fig.Generator = { - script: "firebase projects:list", // this calls to a firebase server and is therefore slow + script: ["firebase","projects:list"], // this calls to a firebase server and is therefore slow postProcess: (out) => { const getAliasRegex = /^│ (\w.*?)│/gm; const aliasesRaw = Array.from(out.matchAll(getAliasRegex)); diff --git a/src/fisher.ts b/src/fisher.ts index 0370177e288e..5dd4efe25d67 100644 --- a/src/fisher.ts +++ b/src/fisher.ts @@ -70,7 +70,7 @@ const pluginList = [ ]; const installedPlugins: Fig.Generator = { - script: "fish -c 'fisher list'", + script: ["fish","-c","fisher list"], postProcess: (output: string) => { if (!output) { return []; diff --git a/src/flutter.ts b/src/flutter.ts index 6a0504c1d4f5..a873363e28b4 100644 --- a/src/flutter.ts +++ b/src/flutter.ts @@ -4,7 +4,7 @@ import { filepaths } from "@fig/autocomplete-generators"; const flutterGenerators: Record = { emulators: { - script: "flutter emulators", + script: ["flutter", "emulators"], postProcess: function (out) { return out .match(/.*•.*/gi) @@ -863,7 +863,7 @@ const completionSpec = { description: "Switch to . Leave this blank to see available channels", generators: { - script: "flutter channel", + script: ["flutter", "channel"], postProcess: function (out) { return out .split("\n") diff --git a/src/flyctl.ts b/src/flyctl.ts index d0df53b2af40..40680fb75ccc 100644 --- a/src/flyctl.ts +++ b/src/flyctl.ts @@ -15,7 +15,7 @@ type FlyApp = { // Autocompletion generator for Fly apps using flyctl list apps // https://fly.io/docs/flyctl/apps-list/ const flyAppsGenerator: Fig.Generator = { - script: "flyctl apps list --json", + script: ["flyctl", "apps", "list", "--json"], postProcess: (output) => { const json: FlyApp[] = JSON.parse(output); @@ -38,7 +38,7 @@ const flyAppsGenerator: Fig.Generator = { // Autocompletion generator for Fly apps using flyctl list orgs // https://fly.io/docs/flyctl/orgs-list/ const flyOrgsGenerator: Fig.Generator = { - script: "fly orgs list --json", + script: ["fly", "orgs", "list", "--json"], postProcess: (output) => { const json: Record = JSON.parse(output); diff --git a/src/fnm.ts b/src/fnm.ts index ecd30a9d0f94..cbcf48a3a616 100644 --- a/src/fnm.ts +++ b/src/fnm.ts @@ -8,7 +8,7 @@ interface NodejsVersion { // Generators const versionGenerator: Fig.Generator = { - script: "fnm list", + script: ["fnm", "list"], postProcess: function (out) { return out .split("\n") @@ -49,7 +49,7 @@ const uniqBy = (arr: T[], callback: (a: T, b: T) => boolean) => * - Every other version, sorted; */ const remoteVersionGenerator: Fig.Generator = { - script: "fnm list-remote", + script: ["fnm", "list-remote"], postProcess: function (out) { const parsed = out .split("\n") diff --git a/src/fvm.ts b/src/fvm.ts index 4ef576bfb300..a8298415e3eb 100644 --- a/src/fvm.ts +++ b/src/fvm.ts @@ -103,7 +103,7 @@ const completionSpec: Fig.Spec = { }, ], generators: { - script: "fvm releases", + script: ["fvm", "releases"], postProcess: function (out): Fig.Suggestion[] { const matches = out.match(semverRegex); const matchesSet = [...new Set(matches)]; diff --git a/src/gem.ts b/src/gem.ts index e484366bdaef..83d84e5e1252 100644 --- a/src/gem.ts +++ b/src/gem.ts @@ -2,13 +2,19 @@ const gems: Fig.Generator = { trigger: () => true, custom: async (tokens, executeShellCommand) => { const searchTerm = tokens[tokens.length - 1]; - const out = await executeShellCommand( - `gem search --both --no-versions --no-details --quiet --norc '${searchTerm.replace( - "'", - `'"'"'` - )}'` - ); - return out + const { stdout } = await executeShellCommand({ + command: "gem", + args: [ + "search", + "--both", + "--no-versions", + "--no-details", + "--quiet", + "--norc", + searchTerm, + ], + }); + return stdout .trim() .split("\n") .filter((line) => line && !line.startsWith("*")) diff --git a/src/gh.ts b/src/gh.ts index 4a41a053701e..9dc0dd09b3b6 100644 --- a/src/gh.ts +++ b/src/gh.ts @@ -89,15 +89,24 @@ const ghGenerators: Record = { if (!userOrOrg) return []; //run `gh repo list` cmd - const res = await execute( - `gh repo list ${userOrOrg} --limit 9999 --json "nameWithOwner,description,isPrivate" ` - ); + const { stdout, status } = await execute({ + command: "gh", + args: [ + "repo", + "list", + userOrOrg, + "--limit", + "9999", + "--json", + "nameWithOwner,description,isPrivate", + ], + }); // make sure it has some existence. - if (!res) return []; + if (status !== 0) return []; //parse the JSON string output of the command - const repoArr: RepoDataType[] = JSON.parse(res); + const repoArr: RepoDataType[] = JSON.parse(stdout); return repoArr.map(listRepoMapFunction); }, @@ -109,8 +118,16 @@ const ghGenerators: Record = { * * --jq https://cli.github.com/manual/gh_help_formatting https://www.baeldung.com/linux/jq-command-json */ - script: - "gh api graphql --paginate -f query='query($endCursor: String) { viewer { repositories(first: 100, after: $endCursor) { nodes { isPrivate, nameWithOwner, description } pageInfo { hasNextPage endCursor }}}}' --jq '.data.viewer.repositories.nodes[]'", + script: [ + "gh", + "api", + "graphql", + "--paginate", + "-f", + "query='query($endCursor: String) { viewer { repositories(first: 100, after: $endCursor) { nodes { isPrivate, nameWithOwner, description } pageInfo { hasNextPage endCursor }}}}'", + "--jq", + ".data.viewer.repositories.nodes[]", + ], postProcess: (out) => { if (out) { /** @@ -139,7 +156,7 @@ const ghGenerators: Record = { }, listPR: { cache: { strategy: "stale-while-revalidate" }, - script: "gh pr list --json=number,title,headRefName,state", + script: ["gh", "pr", "list", "--json=number,title,headRefName,state"], postProcess: (out) => { interface PR { headRefName: string; @@ -160,7 +177,7 @@ const ghGenerators: Record = { }, }, listAlias: { - script: "gh alias list", + script: ["gh", "alias", "list"], postProcess: (out) => { const aliases = out.split("\n").map((line) => { const [name, content] = line.split(":"); @@ -176,8 +193,14 @@ const ghGenerators: Record = { }, }, remoteBranches: { - script: - "git --no-optional-locks branch -r --no-color --sort=-committerdate", + script: [ + "git", + "--no-optional-locks", + "branch", + "-r", + "--no-color", + "--sort=-committerdate", + ], postProcess: postProcessRemoteBranches, }, }; @@ -239,8 +262,11 @@ const completionSpec: Fig.Spec = { generators: ghGenerators.listAlias, parserDirectives: { alias: async (token, executeShellCommand) => { - const out = await executeShellCommand(`gh alias list`); - const alias = out + const { stdout } = await executeShellCommand({ + command: "gh", + args: ["alias", "list"], + }); + const alias = stdout .split("\n") .find((line) => line.startsWith(`${token}:\t`)); diff --git a/src/gibo.ts b/src/gibo.ts index d2301ad93fbb..eabd092b55f7 100644 --- a/src/gibo.ts +++ b/src/gibo.ts @@ -1,5 +1,5 @@ const boilerplates: Fig.Generator = { - script: "gibo list", + script: ["gibo", "list"], splitOn: "\n", }; diff --git a/src/git-cliff.ts b/src/git-cliff.ts index 01060bb363c9..c8d8557ed541 100644 --- a/src/git-cliff.ts +++ b/src/git-cliff.ts @@ -154,7 +154,7 @@ const completionSpec: Fig.Spec = { generators: { trigger: "..", getQueryTerm: ".", - script: "git rev-list --all --oneline --abbrev-commit", + script: ["git", "rev-list", "--all", "--oneline", "--abbrev-commit"], postProcess: (out, tokens) => { if (out.startsWith("fatal:")) { return []; diff --git a/src/git-flow.ts b/src/git-flow.ts index 76db78727e94..a7c51eb7ae26 100644 --- a/src/git-flow.ts +++ b/src/git-flow.ts @@ -23,22 +23,30 @@ const postProcessBranches: Fig.Generator["postProcess"] = (out, tokens) => { async function getGitFlowPrefix( type: string, - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ): Promise { - const prefix: string = await executeShellCommand( - `git config --get gitflow.prefix.${type}` - ); - return prefix; + const { stdout } = await executeShellCommand({ + command: "git", + args: ["config", "--get", `gitflow.prefix.${type}`], + }); + return stdout; } export const gitFlowGenerators: Record = { typeBranches: { custom: async (tokens, executeShellCommand) => { const prefix = await getGitFlowPrefix(tokens[1], executeShellCommand); - const out = await executeShellCommand( - "git --no-optional-locks branch -a --no-color --sort=-committerdate" - ); - return postProcessBranches(out, [prefix]); + const { stdout } = await executeShellCommand({ + command: "git", + args: [ + "--no-optional-locks", + "branch", + "-a", + "--no-color", + "--sort=-committerdate", + ], + }); + return postProcessBranches(stdout, [prefix]); }, }, }; diff --git a/src/git-profile.ts b/src/git-profile.ts index 7224a79ef361..e4c8d2f15eaa 100644 --- a/src/git-profile.ts +++ b/src/git-profile.ts @@ -1,6 +1,6 @@ // https://github.com/dotzero/git-profile const profiles: Fig.Generator = { - script: "git-profile list", + script: ["git-profile", "list"], postProcess: (output) => { return Array.from(output.matchAll(/^\[(.+?)\]$/gm)).map((result) => ({ name: result[1], diff --git a/src/git.ts b/src/git.ts index f4921d492c8b..50dbdcd0fad3 100644 --- a/src/git.ts +++ b/src/git.ts @@ -117,7 +117,7 @@ const postProcessBranches = export const gitGenerators: Record = { // Commit history commits: { - script: "git --no-optional-locks log --oneline", + script: ["git", "--no-optional-locks", "log", "--oneline"], postProcess: function (out) { const output = filterMessages(out); @@ -137,7 +137,7 @@ export const gitGenerators: Record = { // user aliases aliases: { - script: "git --no-optional-locks config --get-regexp '^alias.'", + script: ["git", "--no-optional-locks", "config", "--get-regexp", "^alias."], cache: { strategy: "stale-while-revalidate", }, @@ -163,7 +163,7 @@ export const gitGenerators: Record = { }, revs: { - script: "git rev-list --all --oneline", + script: ["git", "rev-list", "--all", "--oneline"], postProcess: function (out) { const output = filterMessages(out); @@ -184,7 +184,7 @@ export const gitGenerators: Record = { // Saved stashes // TODO: maybe only print names of stashes stashes: { - script: "git --no-optional-locks stash list", + script: ["git", "--no-optional-locks", "stash", "list"], postProcess: function (out) { const output = filterMessages(out); @@ -210,7 +210,7 @@ export const gitGenerators: Record = { // https://mirrors.edge.kernel.org/pub/software/scm/git/docs/#_identifier_terminology treeish: { - script: "git --no-optional-locks diff --cached --name-only", + script: ["git", "--no-optional-locks", "diff", "--cached", "--name-only"], postProcess: function (out, tokens) { const output = filterMessages(out); @@ -231,13 +231,25 @@ export const gitGenerators: Record = { // All branches remoteLocalBranches: { - script: - "git --no-optional-locks branch -a --no-color --sort=-committerdate", + script: [ + "git", + "--no-optional-locks", + "branch", + "-a", + "--no-color", + "--sort=-committerdate", + ], postProcess: postProcessBranches({ insertWithoutRemotes: true }), }, localBranches: { - script: "git --no-optional-locks branch --no-color --sort=-committerdate", + script: [ + "git", + "--no-optional-locks", + "branch", + "--no-color", + "--sort=-committerdate", + ], postProcess: postProcessBranches({ insertWithoutRemotes: true }), }, @@ -248,16 +260,32 @@ export const gitGenerators: Record = { const pp = postProcessBranches({ insertWithoutRemotes: true }); if (tokens.includes("-r")) { return pp( - await executeShellCommand( - "git --no-optional-locks branch -r --no-color --sort=-committerdate" - ), + ( + await executeShellCommand({ + command: "git", + args: [ + "--no-optional-locks", + "-r", + "--no-color", + "--sort=-committerdate", + ], + }) + ).stdout, tokens ); } else { return pp( - await executeShellCommand( - "git --no-optional-locks branch --no-color --sort=-committerdate" - ), + ( + await executeShellCommand({ + command: "git", + args: [ + "--no-optional-locks", + "branch", + "--no-color", + "--sort=-committerdate", + ], + }) + ).stdout, tokens ); } @@ -265,7 +293,7 @@ export const gitGenerators: Record = { }, remotes: { - script: "git --no-optional-locks remote -v", + script: ["git", "--no-optional-locks", "remote", "-v"], postProcess: function (out) { const remoteURLs = out.split("\n").reduce((dict, line) => { const pair = line.split("\t"); @@ -300,7 +328,13 @@ export const gitGenerators: Record = { }, tags: { - script: "git --no-optional-locks tag --list --sort=-committerdate", + script: [ + "git", + "--no-optional-locks", + "tag", + "--list", + "--sort=-committerdate", + ], postProcess: function (output) { return output.split("\n").map((tag) => ({ name: tag, @@ -311,7 +345,7 @@ export const gitGenerators: Record = { // Files for staging files_for_staging: { - script: "git --no-optional-locks status --short", + script: ["git", "--no-optional-locks", "status", "--short"], postProcess: (out, context) => { // This whole function is a mess @@ -416,7 +450,7 @@ export const gitGenerators: Record = { }, getUnstagedFiles: { - script: "git --no-optional-locks diff --name-only", + script: ["git", "--no-optional-locks", "diff", "--name-only"], splitOn: "\n", }, @@ -3980,8 +4014,11 @@ const completionSpec: Fig.Spec = { name: "git", description: "The stupid content tracker", generateSpec: async (_, executeShellCommand) => { - const out = await executeShellCommand("git help -a"); - const lines = out.trim().split("\n"); + const { stdout } = await executeShellCommand({ + command: "git", + args: ["help", "-a"], + }); + const lines = stdout.trim().split("\n"); const start = lines.findIndex((val) => val.match(/external commands/i)); const commands: string[] = []; for (let i = start + 1; i < lines.length; i += 1) { @@ -4005,11 +4042,14 @@ const completionSpec: Fig.Spec = { description: "Custom user defined git alias", parserDirectives: { alias: async (token, exec) => { - const result = await exec(`git config --get alias.${token}`); - if (!result) { + const { stdout, status } = await exec({ + command: "git", + args: ["config", "--get", `alias.${token}`], + }); + if (status !== 0) { throw new Error("Failed parsing alias"); } - return result; + return stdout; }, }, isOptional: true, @@ -4349,19 +4389,30 @@ const completionSpec: Fig.Spec = { name: "message", generators: ai({ name: "git commit -m", - prompt: ({ executeShellCommand }) => { - const gitLogShortMessages = executeShellCommand( - "git log --pretty=format:%s --abbrev-commit --max-count=20" - ); + prompt: async ({ executeCommand }) => { + const { stdout } = await executeCommand({ + command: "git", + args: [ + "log", + "--pretty=format:%s", + "--abbrev-commit", + "--max-count=20", + ], + }); return ( 'Generate a git commit message summary based on this git diff, the "summary" must be no more ' + "than 70-75 characters, and it must describe both what the patch changes, as well as why the " + - `patch might be necessary.\n\nHere are some examples from the repo:\n${gitLogShortMessages}` + `patch might be necessary.\n\nHere are some examples from the repo:\n${stdout}` ); }, - message: ({ executeShellCommand }) => - executeShellCommand("git diff --staged"), + message: async ({ executeCommand }) => + ( + await executeCommand({ + command: "git", + args: ["diff", "--staged"], + }) + ).stdout, splitOn: "\n", }), }, @@ -4856,7 +4907,7 @@ const completionSpec: Fig.Spec = { icon: "⚙️", })), generators: { - script: "git config --get-regexp '.*'", + script: ["git", "config", "--get-regexp", ".*"], // This is inefficient but it doesn't need to be faster - most // of the time, you don't need to run `git config` commands, // and when you do it's typically one or two at most. diff --git a/src/go.ts b/src/go.ts index 30ad0ce7053a..3d48fb6a67a1 100644 --- a/src/go.ts +++ b/src/go.ts @@ -871,7 +871,7 @@ const completionSpec: Fig.Spec = { args: { name: "tool", generators: { - script: "go tool", + script: ["go", "tool"], splitOn: "\n", }, }, diff --git a/src/gource.ts b/src/gource.ts index 853cac492d41..fd7c765fe88b 100644 --- a/src/gource.ts +++ b/src/gource.ts @@ -79,7 +79,7 @@ export interface SpdisplaysDisplayportDevice { const screenNumbers: Fig.Generator = { // one thing to note is that this is MacOS specific - script: "system_profiler SPDisplaysDataType -json", + script: ["system_profiler", "SPDisplaysDataType", "-json"], postProcess: (output) => { if (output.includes("command not found")) { return []; diff --git a/src/gpg.ts b/src/gpg.ts index e702226fa2b1..1bee4eb9027b 100644 --- a/src/gpg.ts +++ b/src/gpg.ts @@ -1,5 +1,5 @@ const getCipherAlgorithms: Fig.Generator = { - script: "gpg --version", + script: ["gpg", "--version"], postProcess: (out, context) => { // Get the substring 2of cyphers, remove whitespace and split by commas let cyphers = out.substring( @@ -15,7 +15,7 @@ const getCipherAlgorithms: Fig.Generator = { }; const getDigestAlgorithms: Fig.Generator = { - script: "gpg --version", + script: ["gpg", "--version"], postProcess: (out, context) => { // Get the substring of digests, remove whitespace and split by commas let digests = out.substring( diff --git a/src/heroku.ts b/src/heroku.ts index b1c85a5d46d5..eb2404c4876a 100644 --- a/src/heroku.ts +++ b/src/heroku.ts @@ -1,5 +1,5 @@ const getAppGenerator: Fig.Generator = { - script: "heroku apps --all --json", + script: ["heroku", "apps", "--all", "--json"], cache: { strategy: "stale-while-revalidate", }, diff --git a/src/hugo.ts b/src/hugo.ts index e921139b59fe..4c9bfc490011 100644 --- a/src/hugo.ts +++ b/src/hugo.ts @@ -874,7 +874,7 @@ const completionSpec: Fig.Spec = { { name: "archetype|default", generators: { - script: "ls ./archetypes/", + script: ["ls","./archetypes/"], postProcess: (output) => output.split("\n").map((fileName) => ({ name: fileName.slice(0, fileName.lastIndexOf(".")), diff --git a/src/hyper.ts b/src/hyper.ts index 5c014c8dfa45..e28b4f1214bb 100644 --- a/src/hyper.ts +++ b/src/hyper.ts @@ -47,7 +47,7 @@ const completionSpec: Fig.Spec = { name: "plugin", description: "Plugin to uninstall", generators: { - script: "hyper list", + script: ["hyper", "list"], postProcess: function (out) { return out.split("\n").map((p) => { return { name: p, description: "Plugin name" }; diff --git a/src/ibus.ts b/src/ibus.ts index 1509af559823..0ad64e1dc95c 100644 --- a/src/ibus.ts +++ b/src/ibus.ts @@ -7,7 +7,7 @@ const completionSpec: Fig.Spec = { args: { isOptional: true, generators: { - script: "ibus list-engine", + script: ["ibus", "list-engine"], postProcess: (out) => out .split("\n") diff --git a/src/ignite-cli.ts b/src/ignite-cli.ts index 5143ea118306..d986e98a5a00 100644 --- a/src/ignite-cli.ts +++ b/src/ignite-cli.ts @@ -1,5 +1,5 @@ const generatorsGenerator: Fig.Generator = { - script: "ls ignite/templates", + script: ["ls","ignite/templates"], postProcess: (out) => { if (out.trim() === "") return []; return out.split("\n").map((gen) => ({ diff --git a/src/infracost/index.ts b/src/infracost/index.ts index 709cd06be1bb..3a2c28572500 100644 --- a/src/infracost/index.ts +++ b/src/infracost/index.ts @@ -5,8 +5,12 @@ const versionFiles = ["0.9.0"]; export const getVersionCommand: Fig.GetVersionCommand = async ( executeShellCommand ) => { - const out = await executeShellCommand("infracost --version"); - return clean(out.slice(out.indexOf(" ") + 1)); + const { stdout } = await executeShellCommand({ + command: "infracost", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--version"], + }); + return clean(stdout.slice(stdout.indexOf(" ") + 1)); }; export default createVersionedSpec("infracost", versionFiles); diff --git a/src/ipatool.ts b/src/ipatool.ts index 70fb70187397..c398edc0a838 100644 --- a/src/ipatool.ts +++ b/src/ipatool.ts @@ -16,9 +16,17 @@ type App = { const bundleIdentifierGenerator: Fig.Generator = { script: (context) => { const identifier = context[context.length - 1]; - if (!identifier) return ""; + if (!identifier) return undefined; - return `ipatool search ${identifier} --limit 10 --format json`; + return [ + "ipatool", + "search", + identifier, + "--limit", + "10", + "--format", + "json", + ]; }, postProcess: (output) => { if (!output) return []; diff --git a/src/j.ts b/src/j.ts index 16b6fd668452..e2726ca08381 100644 --- a/src/j.ts +++ b/src/j.ts @@ -8,9 +8,15 @@ const completionSpec: Fig.Spec = { description: "Directory to jump to", isVariadic: true, generators: { - script: 'cat "$HOME"/Library/autojump/autojump.txt', - postProcess: (out, ctx) => { - const lines = out.split("\n").map((line) => { + custom: async (tokens, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [ + `${context.environmentVariables["HOME"]}/Library/autojump/autojump.txt`, + ], + }); + const lines = stdout.split("\n").map((line) => { const [weight, dir] = line.split("\t"); return { @@ -19,7 +25,7 @@ const completionSpec: Fig.Spec = { }; }); - const args = ctx.slice(1, ctx.length - 1); + const args = tokens.slice(1, tokens.length - 1); // directory arg is variadic with each subsequent arg being an // additional filter. filtered will filter directories by all args. diff --git a/src/jenv.ts b/src/jenv.ts index 9d6bb4e7c2ea..6cfca182549d 100644 --- a/src/jenv.ts +++ b/src/jenv.ts @@ -12,7 +12,7 @@ const programGenerator: Fig.Generator = { })), }; const generateAllShims: Fig.Generator = { - script: "jenv shims --short", + script: ["jenv", "shims", "--short"], postProcess: function (out) { return out .split("\n") @@ -25,7 +25,7 @@ const generateAllShims: Fig.Generator = { }, }; const generateAllCommands: Fig.Generator = { - script: "jenv commands", + script: ["jenv", "commands"], postProcess: function (out) { return out .split("\n") @@ -40,7 +40,7 @@ const generateAllCommands: Fig.Generator = { }, }; const generateAllPlugins: Fig.Generator = { - script: "jenv plugins", + script: ["jenv", "plugins"], postProcess: function (out) { return out .split("\n") @@ -54,7 +54,7 @@ const generateAllPlugins: Fig.Generator = { }, }; const generateJEnvVersions: Fig.Generator = { - script: "jenv versions --bare", + script: ["jenv","versions","--bare"], postProcess: function (out) { return out .split("\n") diff --git a/src/just.ts b/src/just.ts index ae72d8d104c9..172e3dd817dd 100644 --- a/src/just.ts +++ b/src/just.ts @@ -85,10 +85,18 @@ function getJustfilePath(tokens: string[]): string | null { * Get the command to dump the justfile at the given path, or let `just` handle * searching for the file if the path is null. */ -function getJustfileDumpCommand(justfilePath: string | null): string { +function getJustfileDumpCommand(justfilePath: string | null): string[] { return justfilePath - ? `just --unstable --dump --dump-format json --justfile '${justfilePath}'` - : `just --unstable --dump --dump-format json`; + ? [ + "just", + "--unstable", + "--dump", + "--dump-format", + "json", + "--justfile", + justfilePath, + ] + : ["just", "--unstable", "--dump", "--dump-format", "json"]; } /** diff --git a/src/k3d.ts b/src/k3d.ts index 2a649c26c031..9cf64c293762 100644 --- a/src/k3d.ts +++ b/src/k3d.ts @@ -1,5 +1,5 @@ const ClusterGenerator: Fig.Generator = { - script: "k3d cluster list --no-headers", + script: ["k3d", "cluster", "list", "--no-headers"], postProcess: (out) => { return out.split("\n").map((line) => { const [name, servers, agents] = line.split(/\s+/); @@ -34,7 +34,7 @@ const ShellCompletions: Fig.Suggestion[] = [ // Docker Image Generator const DockerImageGenerator: Fig.Generator = { - script: "docker image ls --format '{{.Repository}}:{{.Tag}}'", + script: ["docker", "image", "ls", "--format", "{{.Repository}}:{{.Tag}}"], postProcess: (out) => { return out.split("\n").map((image) => ({ name: image, @@ -46,7 +46,7 @@ const DockerImageGenerator: Fig.Generator = { // Node Generator const NodeGenerator: Fig.Generator = { - script: "k3d node list --no-headers", + script: ["k3d", "node", "list", "--no-headers"], postProcess: (out) => { return out.split("\n").map((line) => { const [name, role, cluster] = line.split(/\s+/); @@ -61,7 +61,7 @@ const NodeGenerator: Fig.Generator = { // Registry Generator const RegistryGenerator: Fig.Generator = { - script: "k3d registry list --no-headers", + script: ["k3d", "registry", "list", "--no-headers"], postProcess: (out) => { return out.split("\n").map((line) => { const [name, cluster] = line.split(/\s+/); diff --git a/src/k9s.ts b/src/k9s.ts index e32d21f07c6c..f13c859e3744 100644 --- a/src/k9s.ts +++ b/src/k9s.ts @@ -1,5 +1,5 @@ const namespaces: Fig.Generator = { - script: "kubectl get namespaces", + script: ["kubectl", "get", "namespaces"], postProcess: (out) => { return out .split("\n") diff --git a/src/kill.ts b/src/kill.ts index 17a4db2b65ea..425a35cfcf21 100644 --- a/src/kill.ts +++ b/src/kill.ts @@ -38,7 +38,7 @@ const completionSpec: Fig.Spec = { name: "signal_name", generators: { // Bash's `kill` builtin has different output to /bin/kill - script: "env kill -l", + script: ["env", "kill", "-l"], postProcess: (out) => out.match(/\w+/g).map((name) => ({ name, diff --git a/src/kind.ts b/src/kind.ts index 6f4794ceea15..848571cd9a57 100644 --- a/src/kind.ts +++ b/src/kind.ts @@ -1,5 +1,5 @@ const ClusterGenerator: Fig.Generator = { - script: "kind get clusters", + script: ["kind", "get", "clusters"], postProcess: (out) => { return out.split("\n").map((cluster) => ({ name: cluster, @@ -9,7 +9,7 @@ const ClusterGenerator: Fig.Generator = { }; const NodeGenerator: Fig.Generator = { - script: "kind get nodes -A", + script: ["kind", "get", "nodes", "-A"], postProcess: (out) => { return out.split("\n").map((node) => ({ name: node, diff --git a/src/kool.ts b/src/kool.ts index 0392bd8871b9..85c68e7424c3 100644 --- a/src/kool.ts +++ b/src/kool.ts @@ -1,5 +1,5 @@ const getScripts: Fig.Generator = { - script: "kool run --help", + script: ["kool", "run", "--help"], postProcess: (output) => { const lines = output.split("\n"); const scriptsIndex = lines.findIndex( @@ -14,7 +14,7 @@ const getScripts: Fig.Generator = { }; const getServices: Fig.Generator = { - script: "docker-compose config --services", + script: ["docker-compose", "config", "--services"], splitOn: "\n", }; diff --git a/src/kubectl.ts b/src/kubectl.ts index dbd092a511b1..06dc2740e879 100644 --- a/src/kubectl.ts +++ b/src/kubectl.ts @@ -1,8 +1,8 @@ // Internal scripts for this spec, not to be confused with the script property const scripts = { - types: "kubectl api-resources -o name", + types: ["kubectl", "api-resources", "-o", "name"], typeWithoutName: function (type) { - return `kubectl get ${type} -o custom-columns=:.metadata.name`; + return ["kubectl", "get", type, "-o", "custom-columns=:.metadata.name"]; }, }; @@ -43,7 +43,14 @@ const sharedArgs: Record = { runningPodsArg: { name: "Running Pods", generators: { - script: "kubectl get pods --field-selector=status.phase=Running -o name", + script: [ + "kubectl", + "get", + "pods", + "--field-selector=status.phase=Running", + "-o", + "name", + ], postProcess: sharedPostProcess, }, }, @@ -68,11 +75,16 @@ const sharedArgs: Record = { script: function (context) { const index = context.indexOf("--kubeconfig"); if (index !== -1) { - return `kubectl config --kubeconfig=${ - context[index + 1] - } get-contexts -o name`; + return [ + "kubectl", + "config", + `--kubeconfig=${context[index + 1]}`, + "get-contexts", + "-o", + "name", + ]; } - return "kubectl config get-contexts -o name"; + return ["kubectl", "config", "get-contexts", "-o", "name"]; }, postProcess: sharedPostProcess, }, @@ -94,11 +106,14 @@ const sharedArgs: Record = { script: function (context) { const index = context.indexOf("--kubeconfig"); if (index !== -1) { - return `kubectl config --kubeconfig=${ - context[index + 1] - } get-clusters`; + return [ + "kubectl", + "config", + `--kubeconfig=${context[index + 1]}`, + "get-clusters", + ]; } - return "kubectl config get-clusters"; + return ["kubectl", "config", "get-clusters"]; }, postProcess: function (out) { if ( @@ -174,7 +189,7 @@ const sharedArgs: Record = { const podName = context[podIndex].includes("/") ? context[podIndex] : `${context[podIndex]} + ${context[podIndex + 1]}`; - return `kubectl get ${podName} -o json`; + return ["kubectl", "get", podName, "-o", "json"]; }, postProcess: function (out) { if ( diff --git a/src/kubectx.ts b/src/kubectx.ts index 02343994410e..ea5450abe4fc 100644 --- a/src/kubectx.ts +++ b/src/kubectx.ts @@ -33,7 +33,7 @@ const completionSpec: Fig.Spec = { name: "context", isVariadic: true, generators: { - script: `kubectx`, + script: ["kubectx"], postProcess: (out) => { const contexts = out.split("\n").map((item) => ({ name: item, @@ -68,7 +68,7 @@ const completionSpec: Fig.Spec = { })) as Fig.Suggestion[], }, { - script: `kubectx -c`, + script: ["kubectx", "-c"], postProcess: (out) => { return !out ? [] diff --git a/src/kubens.ts b/src/kubens.ts index 72518d979a29..17437c6c1949 100644 --- a/src/kubens.ts +++ b/src/kubens.ts @@ -35,7 +35,7 @@ const completionSpec: Fig.Spec = { })) as Fig.Suggestion[], }, { - script: "kubens -c", + script: ["kubens", "-c"], postProcess: (out) => { return !out ? [] diff --git a/src/launchctl.ts b/src/launchctl.ts index fedbb680370f..957910e8354e 100644 --- a/src/launchctl.ts +++ b/src/launchctl.ts @@ -54,7 +54,7 @@ const limitArgs = [ ]; const listGenerator: Fig.Generator = { - script: "launchctl list", + script: ["launchctl", "list"], // The list command outputs 3 columns: PID Status Label // we want the last column ([2]) postProcess: function (out) { diff --git a/src/lerna.ts b/src/lerna.ts index d47b354de055..b3ffc82f6065 100644 --- a/src/lerna.ts +++ b/src/lerna.ts @@ -1,7 +1,5 @@ -const SPLIT_CHAR = "END"; - const getPackages: Fig.Generator = { - script: "lerna ls", + script: ["lerna", "ls"], postProcess: (output) => output.split("\n").map((packageName) => ({ name: packageName, @@ -10,7 +8,7 @@ const getPackages: Fig.Generator = { }; const getBranches: Fig.Generator = { - script: "git branch --no-color", + script: ["git", "branch", "--no-color"], postProcess: function (out) { if (out.startsWith("fatal:")) { return []; @@ -27,10 +25,14 @@ const getBranches: Fig.Generator = { const getAllScriptsFromPackages: Fig.Generator = { // Get all lerna packages, loop over them and get content of package.json - script: `lerna list -p | while read p; do\n \\cat $p/package.json && echo ${SPLIT_CHAR}\ndone`, + script: [ + "bash", + "-c", + "lerna list -p | while read p; do\n \\cat $p/package.json && echo END\ndone", + ], postProcess: (output) => { // Split output by the divider and remove empty entry - const packages = output.split(SPLIT_CHAR).filter((e) => e.trim() !== ""); + const packages = output.split("END").filter((e) => e.trim() !== ""); let scripts: string[] = []; packages.forEach((packageContent) => { @@ -536,7 +538,7 @@ const completionSpec: Fig.Spec = { args: { name: "remote", generators: { - script: "git remote", + script: ["git", "remote"], postProcess: (output) => { return output.split("\n").map((remoteName) => ({ name: remoteName, diff --git a/src/limactl.ts b/src/limactl.ts index 2a21b9bff54b..d771a1735878 100644 --- a/src/limactl.ts +++ b/src/limactl.ts @@ -16,7 +16,7 @@ const generateGlobalFlags = (subcommandName: string): Fig.Option[] => [ const instanceNameGenerator = ( suggestOptions?: Partial ): Fig.Generator => ({ - script: "limactl list --quiet", + script: ["limactl", "list", "--quiet"], postProcess: (output) => output.split("\n").map((instanceName) => ({ name: `${instanceName}`, diff --git a/src/login.ts b/src/login.ts index f5d24068b03d..8055381a3b57 100644 --- a/src/login.ts +++ b/src/login.ts @@ -25,7 +25,7 @@ const completionSpec: Fig.Spec = { args: { name: "username", generators: { - script: "cat /etc/passwd", + script: ["cat","/etc/passwd"], postProcess: (out) => { return out.split("\n").map((line) => { const [username] = line.split(":"); diff --git a/src/lsof.ts b/src/lsof.ts index f25dcf338c22..d77498909763 100644 --- a/src/lsof.ts +++ b/src/lsof.ts @@ -244,7 +244,7 @@ const completionSpec: Fig.Spec = { name: "options", generators: [ { - script: "echo", + script: ["echo"], postProcess: function () { const startParams = ["4", "6"]; return startParams.map((param) => ({ @@ -253,7 +253,7 @@ const completionSpec: Fig.Spec = { }, }, { - script: "echo", + script: ["echo"], postProcess: function (out, tokens) { const startParams = ["tcp", "udp", "TCP", "UDP"]; const token = @@ -267,7 +267,7 @@ const completionSpec: Fig.Spec = { }, }, { - script: "ifconfig", + script: ["ifconfig"], postProcess: function (out, tokens) { const ips = out .split("\n") @@ -291,7 +291,7 @@ const completionSpec: Fig.Spec = { trigger: "@", }, { - script: "echo", + script: ["echo"], postProcess: function (out, tokens) { const colonParams = ["http", "https", "who", "time"]; let token = ":"; diff --git a/src/m.ts b/src/m.ts index dc6f328a0498..d255cb37bb7d 100644 --- a/src/m.ts +++ b/src/m.ts @@ -1,10 +1,10 @@ const generateDisks: Fig.Generator = { - // ? is a bash/fish/zsh glob pattern for "any character, exactly once" - script: "command ls /dev/disk?", + script: ["ls", "/dev"], postProcess: (out) => out .trim() .split("\n") + .filter((disk) => disk.match(/\/dev\/disk\w/)) .map((disk) => ({ name: disk, icon: "💽", @@ -13,14 +13,14 @@ const generateDisks: Fig.Generator = { }; const generateVolumes: Fig.Generator = { - script: "command ls -d /Volumes/*", + script: ["ls", "/Volumes"], postProcess: (out) => out .trim() .split("\n") .filter((volume) => volume !== "Macintosh HD") .map((volume) => ({ - name: volume, + name: `/Volumes/${volume}`, type: "file", priority: 100, })), diff --git a/src/mackup.ts b/src/mackup.ts index c09bc97bfb12..90e19fb4659e 100644 --- a/src/mackup.ts +++ b/src/mackup.ts @@ -1,5 +1,5 @@ const applicationGenerator: Fig.Generator = { - script: "mackup list", + script: ["mackup", "list"], postProcess: (output) => { return output .split("\n") diff --git a/src/mamba.ts b/src/mamba.ts index 9e43c54c9ebc..f0894dd089f5 100644 --- a/src/mamba.ts +++ b/src/mamba.ts @@ -37,7 +37,7 @@ interface SearchItem { const getMambaEnvs: Fig.Generator = { // For some reason the json version of this command // does not give out the names, so here we are - script: "conda env list", + script: ["conda", "env", "list"], scriptTimeout: 10000, cache: { strategy: "stale-while-revalidate", @@ -61,7 +61,7 @@ const getMambaEnvs: Fig.Generator = { }; const getInstalledPackages: Fig.Generator = { - script: "conda list --json", + script: ["conda", "list", "--json"], scriptTimeout: 10000, cache: { strategy: "stale-while-revalidate", @@ -87,7 +87,7 @@ const getInstalledPackages: Fig.Generator = { const condaSearchGenerator: Fig.Generator = { script: (context) => { const searchTerm = context[context.length - 1]; - return `conda search ${searchTerm} --json`; + return ["conda", "search", searchTerm, "--json"]; }, scriptTimeout: 10000, postProcess(out) { diff --git a/src/man.ts b/src/man.ts index d163f0af48c7..eb4c913006d0 100644 --- a/src/man.ts +++ b/src/man.ts @@ -51,11 +51,14 @@ const generateManualPages: Fig.Generator = { isGeneratingSuggestions = true; // Same as `apropos .`, lists all manual pages with a brief description - const lines = await executeShellCommand("man -k . 2>/dev/null"); + const { stdout } = await executeShellCommand({ + command: "man", + args: ["-k", "."], + }); const seenPageNameCache = new Set(); // Guaranteed to be one per line - for (const line of lines.split("\n")) { + for (const line of stdout.split("\n")) { const splitIndex = line.indexOf(" - "); const pageNames = line.slice(0, splitIndex); diff --git a/src/mdfind.ts b/src/mdfind.ts index d9839fb59a13..449d35c1ec2e 100644 --- a/src/mdfind.ts +++ b/src/mdfind.ts @@ -1,17 +1,28 @@ const smartFolderGenerator: Fig.Generator = { // `mdfind -s` only accepts smart folders in ~/Library/Saved\ Searches/ - script: `ls -1A ~/Library/Saved\\ Searches/*.savedSearch`, - postProcess: function (files) { - return files.split("\n").map((path) => { - const components = path.split("/"); - const filename = components[components.length - 1]; - return { - name: filename.substring(0, filename.indexOf(".")), // .savedSearch automatically added to the query, so remove it - displayName: filename, - icon: "fig://" + path, - description: "Smart folder", - }; + + custom: async (_, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "ls", + args: [ + "-1A", + `${context.environmentVariables["HOME"]}/Library/Saved Searches/`, + ], }); + + return stdout + .split("\n") + .filter((file) => file.endsWith("savedSearch")) + .map((path) => { + const components = path.split("/"); + const filename = components[components.length - 1]; + return { + name: filename.substring(0, filename.indexOf(".")), // .savedSearch automatically added to the query, so remove it + displayName: filename, + icon: "fig://" + path, + description: "Smart folder", + }; + }); }, trigger: "/", }; diff --git a/src/meteor.ts b/src/meteor.ts index a41452bb1a07..76db94b3a771 100644 --- a/src/meteor.ts +++ b/src/meteor.ts @@ -1,7 +1,7 @@ import { filepaths } from "@fig/autocomplete-generators"; const examplesGenerator: Fig.Generator = { - script: "meteor create --list", + script: ["meteor", "create", "--list"], postProcess: (output) => { return output .split("\n") @@ -13,7 +13,7 @@ const examplesGenerator: Fig.Generator = { }; const packagesGenerator: Fig.Generator = { - script: "cat ./.meteor/packages", + script: ["cat", "./.meteor/packages"], postProcess: (output) => { if (output.includes("No such file or directory")) { return []; @@ -32,7 +32,7 @@ const packagesGenerator: Fig.Generator = { }; const platformGenerator: Fig.Generator = { - script: "meteor list-platforms", + script: ["meteor", "list-platforms"], postProcess: (output) => { return output.split("\n").map((platform) => { return { name: platform }; diff --git a/src/mix.ts b/src/mix.ts index 128ceb6d8e89..d09fe995cc4a 100644 --- a/src/mix.ts +++ b/src/mix.ts @@ -139,7 +139,7 @@ const completionSpec: Fig.Spec = { description: "Prints documentation for a given task", generators: { cache: { ttl: 10000 }, - script: "mix help", + script: ["mix", "help"], postProcess: makeTaskSuggestions, }, }, @@ -165,7 +165,7 @@ const completionSpec: Fig.Spec = { isOptional: true, generators: { cache: { ttl: 10000 }, - script: "mix help", + script: ["mix", "help"], postProcess: makeTaskSuggestions, }, }, diff --git a/src/mkinitcpio.ts b/src/mkinitcpio.ts index a6a2dc658c5e..f87b1e902a4a 100644 --- a/src/mkinitcpio.ts +++ b/src/mkinitcpio.ts @@ -93,7 +93,7 @@ const completionSpec: Fig.Spec = { args: { name: "preset", generators: { - script: "ls /etc/mkinitcpio.d", + script: ["ls","/etc/mkinitcpio.d"], postProcess: (out) => out .trim() diff --git a/src/mount.ts b/src/mount.ts index 86077f508300..9464c22f3637 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -7,7 +7,7 @@ const completionSpec: Fig.Spec = { template: "filepaths", generators: [ { - script: "cat /proc/partitions", // this way we don't depend on lsblk + script: ["cat", "/proc/partitions"], // this way we don't depend on lsblk postProcess: (out) => { return out .trim() @@ -21,7 +21,7 @@ const completionSpec: Fig.Spec = { }, }, { - script: "\\command ls -1 /dev/mapper", // usually LUKS encrypted partitions are here + script: ["ls", "-1", "/dev/mapper"], // usually LUKS encrypted partitions are here postProcess: (out) => { return out .trim() diff --git a/src/multipass.ts b/src/multipass.ts index f48a269c4cbb..1ef9c36c4cf0 100644 --- a/src/multipass.ts +++ b/src/multipass.ts @@ -44,7 +44,7 @@ const sharedOpts: Record = { const multipassGenerators: Record = { allAvailableImages: { - script: "multipass find --format=json", + script: ["multipass","find","--format=json"], postProcess: (out) => { const images = JSON.parse(out).images; return Object.keys(images).map((key) => { @@ -56,7 +56,7 @@ const multipassGenerators: Record = { }, }, allAvailableInstances: { - script: "multipass list --format=json", + script: ["multipass","list","--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state !== "Deleted") { @@ -69,7 +69,7 @@ const multipassGenerators: Record = { }, }, allRunningInstances: { - script: "multipass list --format=json", + script: ["multipass","list","--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Running") { @@ -82,7 +82,7 @@ const multipassGenerators: Record = { }, }, allStoppedInstances: { - script: "multipass list --format=json", + script: ["multipass","list","--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Stopped") { @@ -95,7 +95,7 @@ const multipassGenerators: Record = { }, }, allDeletedInstances: { - script: "multipass list --format=json", + script: ["multipass","list","--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Deleted") { diff --git a/src/n.ts b/src/n.ts index 2eba2e4500d6..1d58f0d7fd84 100644 --- a/src/n.ts +++ b/src/n.ts @@ -22,7 +22,7 @@ const versionArg: Fig.Arg = { }, ], generators: { - script: "n lsr --all", + script: ["n", "lsr", "--all"], postProcess: function (out) { const set = new Set(); const versions = out.split("\n").slice(1); diff --git a/src/networkQuality.ts b/src/networkQuality.ts index a2c77d043ba2..938a88ba6123 100644 --- a/src/networkQuality.ts +++ b/src/networkQuality.ts @@ -31,7 +31,7 @@ const completionSpec: Fig.Spec = { args: { name: "interface", generators: { - script: "networksetup -listallhardwareports", + script: ["networksetup", "-listallhardwareports"], postProcess: (out) => { const suggestions: Fig.Suggestion[] = []; const re = /^Hardware Port: (.*?)\n.*?Device: (.*?)$/gms; diff --git a/src/ng.ts b/src/ng.ts index 2c03fa0f3213..95ac2dc9cf53 100644 --- a/src/ng.ts +++ b/src/ng.ts @@ -3,7 +3,7 @@ interface ProjectDetails { } const projectsGenerator = { - script: "ng config projects", + script: ["ng", "config", "projects"], postProcess: function (out) { try { const projects = JSON.parse(out); diff --git a/src/npx.ts b/src/npx.ts index f84cbd030ccb..6ddd1844565e 100644 --- a/src/npx.ts +++ b/src/npx.ts @@ -166,7 +166,11 @@ const completionSpec: Fig.Spec = { name: "command", isCommand: true, generators: { - script: `until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/`, + script: [ + "bash", + "-c", + "until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/", + ], postProcess: function (out) { const cli = [...npxSuggestions].reduce( (acc, { name }) => [...acc, name], diff --git a/src/ns.ts b/src/ns.ts index c30be334ce9f..c4518f2d3c0e 100644 --- a/src/ns.ts +++ b/src/ns.ts @@ -27,7 +27,7 @@ const deviceOption: Fig.Option = { name: "device id", // TODO: create a generator of ns device --available-devices // generators: { - // script: "ns devices --json", + // script: ["ns", "devices", "--json"], // postProcess: (output) => { // return [{ // name: "test1", @@ -545,8 +545,11 @@ const createCommand: Fig.Subcommand = { args: { name: "template", generators: { - script: - "curl https://api.github.com/repos/NativeScript/nativescript-app-templates/contents/packages", + script: [ + "curl", + "-sfL", + "https://api.github.com/repos/NativeScript/nativescript-app-templates/contents/packages", + ], cache: { ttl: 100 * 24 * 60 * 60 * 1000, // 100days }, @@ -1178,7 +1181,7 @@ const deviceCommand: Fig.Subcommand = { name: "application id", description: "The application identifier", //TODO: generator $ tns device list-applications. // generators: { - // script: "ns device list-applications", + // script: ["ns", "device", "list-applications"], // postProcess: (output) => { // return JSON.parse(output).map((branch) => { // const template = branch?.name; diff --git a/src/nx.ts b/src/nx.ts index 77e522883906..2dd9b1ff73fa 100644 --- a/src/nx.ts +++ b/src/nx.ts @@ -274,7 +274,7 @@ const nxGenerators: NxGenerators = { }, }, list: { - script: "nx list", + script: ["nx", "list"], cache: oneDayCache, postProcess: (out) => { if (out.indexOf("Installed plugins") > -1) { diff --git a/src/okteto.ts b/src/okteto.ts index 030c60fdae95..afb98fdd32c8 100644 --- a/src/okteto.ts +++ b/src/okteto.ts @@ -1,5 +1,5 @@ const contexts: Fig.Generator = { - script: "okteto context list", + script: ["okteto", "context", "list"], cache: { ttl: 1000 * 60 * 30, // 30 minutes }, @@ -19,7 +19,7 @@ const contexts: Fig.Generator = { }; const namespaces: Fig.Generator = { - script: "okteto namespace list", + script: ["okteto", "namespace", "list"], cache: { ttl: 1000 * 60 * 30, // 30 minutes }, diff --git a/src/op.ts b/src/op.ts index 8a51c6e012ab..319ffc1e8bee 100644 --- a/src/op.ts +++ b/src/op.ts @@ -9,7 +9,7 @@ interface Account { } const suggestAccounts: Fig.Generator = { - script: "op account list --format json", + script: ["op", "account", "list", "--format", "json"], postProcess: (out) => { const json = JSON.parse(out) as Account[]; return json.map((account) => ({ diff --git a/src/open.ts b/src/open.ts index 4b03fd1bd68d..66aec9be424d 100644 --- a/src/open.ts +++ b/src/open.ts @@ -1,6 +1,11 @@ export const generateApps = (unquotedPath: string): Fig.Generator => ({ cache: { strategy: "stale-while-revalidate" }, - script: `mdfind kMDItemContentTypeTree=com.apple.application-bundle -onlyin ${unquotedPath}`, + script: [ + "mdfind", + "kMDItemContentTypeTree=com.apple.application-bundle", + "-onlyin", + unquotedPath, + ], postProcess: (out) => { return out.split("\n").map((path) => { const basename = path.slice(path.lastIndexOf("/") + 1); @@ -21,7 +26,11 @@ export const generateApps = (unquotedPath: string): Fig.Generator => ({ export const generateBundleIds = (unquotedPath: string): Fig.Generator => ({ scriptTimeout: 15000, cache: { strategy: "stale-while-revalidate" }, - script: `bash -c 'mdfind kMDItemContentTypeTree=com.apple.application-bundle -onlyin ${unquotedPath} | while read line; do echo $(mdls -name kMDItemCFBundleIdentifier -r "$line") $line; done'`, + script: [ + "bash", + "-c", + `mdfind kMDItemContentTypeTree=com.apple.application-bundle -onlyin ${unquotedPath} | while read line; do echo $(mdls -name kMDItemCFBundleIdentifier -r "$line") $line; done`, + ], postProcess: (out) => { const ids = new Map( out.split("\n").map((line) => { diff --git a/src/pandoc.ts b/src/pandoc.ts index 530507d0db3a..e2509252dc19 100644 --- a/src/pandoc.ts +++ b/src/pandoc.ts @@ -1,36 +1,52 @@ import { filepaths } from "@fig/autocomplete-generators"; -const pandocGenerators: Record = { - inputFormats: { - script: "pandoc --list-input-formats", - postProcess: function (out) { - return out.split("\n").map((format) => ({ - name: format, - icon: `fig://icon?type=${format}`, - })); +const pandocGenerators: Record = { + inputFormats: [ + { + script: ["pandoc", "--list-input-formats"], + postProcess: function (out) { + return out.split("\n").map((format) => ({ + name: format, + icon: `fig://icon?type=${format}`, + })); + }, }, - }, - outputFormats: { - script: "pandoc --list-output-formats", - postProcess: function (out) { - return out.split("\n").map((format) => ({ - name: format, - icon: `fig://icon?type=${format}`, - })); + ], + outputFormats: [ + { + script: ["pandoc", "--list-output-formats"], + postProcess: function (out) { + return out.split("\n").map((format) => ({ + name: format, + icon: `fig://icon?type=${format}`, + })); + }, }, - }, - formats: { - script: "pandoc --list-input-formats && pandoc --list-output-formats", - postProcess: function (out) { - const uniqueFormats = Array.from(new Set(out.split("\n"))); - return uniqueFormats.map((format) => ({ - name: format, - icon: `fig://icon?type=${format}`, - })); + ], + formats: [ + { + script: ["pandoc", "--list-input-formats"], + postProcess: function (out) { + const uniqueFormats = Array.from(new Set(out.split("\n"))); + return uniqueFormats.map((format) => ({ + name: format, + icon: `fig://icon?type=${format}`, + })); + }, }, - }, - yamlFiles: filepaths({ extensions: ["yaml"] }), - yamlJSONFiles: filepaths({ extensions: ["yaml", "json"] }), + { + script: ["pandoc", "--list-output-formats"], + postProcess: function (out) { + const uniqueFormats = Array.from(new Set(out.split("\n"))); + return uniqueFormats.map((format) => ({ + name: format, + icon: `fig://icon?type=${format}`, + })); + }, + }, + ], + yamlFiles: [filepaths({ extensions: ["yaml"] })], + yamlJSONFiles: [filepaths({ extensions: ["yaml", "json"] })], }; const styleFileArg: Fig.Arg = { diff --git a/src/pass.ts b/src/pass.ts index b1f57990fc4d..fbfce12b569a 100644 --- a/src/pass.ts +++ b/src/pass.ts @@ -1,9 +1,16 @@ const listPasswords: Fig.Generator = { - script: function () { - return `grep -r -l '' $HOME/.password-store --exclude-dir=.git`; - }, - postProcess: (output) => { - return output.split("\n").map((password) => ({ + custom: async (_tokens, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "grep", + args: [ + "-r", + "-l", + "", + `${context.environmentVariables["HOME"]}/.password-store`, + "--exclude-dir=.git", + ], + }); + return stdout.split("\n").map((password) => ({ name: password.split(".password-store/").pop().replace(".gpg", ""), icon: "🔐", })); @@ -11,11 +18,15 @@ const listPasswords: Fig.Generator = { }; const listDirectories: Fig.Generator = { - script: function () { - return `ls -dR1a $HOME/.password-store/*/`; - }, - postProcess: (output) => { - return output.split("\n").map((dir) => ({ + custom: async (_tokens, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "ls", + args: [ + "-dR1a", + `${context.environmentVariables["HOME"]}/.password-store`, + ], + }); + return stdout.split("\n").map((dir) => ({ name: dir.split(".password-store/").pop(), icon: "📁", })); diff --git a/src/php.ts b/src/php.ts index f8e1497cf3e6..ab600468cfea 100644 --- a/src/php.ts +++ b/src/php.ts @@ -1,22 +1,41 @@ // To learn more about Fig's autocomplete standard visit: https://fig.io/docs/concepts/cli-skeleton +const fileExists = async ( + executeCommand: Fig.ExecuteCommandFunction, + file: string +) => { + return ( + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + (await executeCommand({ command: "ls", args: [file] })).status === 0 + ); +}; + const completionSpec: Fig.Spec = { name: "php", description: "Run the PHP interpreter", generateSpec: async (tokens, executeShellCommand) => { const subcommands = []; - if ((await executeShellCommand("ls -1 artisan")) === "artisan") { - subcommands.push({ name: "artisan", loadSpec: "php/artisan" }); - } - - if ((await executeShellCommand("ls -1 please")) === "please") { - subcommands.push({ name: "please", loadSpec: "php/please" }); - } - - if ((await executeShellCommand("ls -1 bin/console")) === "bin/console") { - subcommands.push({ name: "bin/console", loadSpec: "php/bin-console" }); - } + await Promise.all([ + async () => { + if (await fileExists(executeShellCommand, "artisan")) { + subcommands.push({ name: "artisan", loadSpec: "php/artisan" }); + } + }, + async () => { + if (await fileExists(executeShellCommand, "please")) { + subcommands.push({ name: "please", loadSpec: "php/please" }); + } + }, + async () => { + if (await fileExists(executeShellCommand, "bin/console")) { + subcommands.push({ + name: "bin/console", + loadSpec: "php/bin-console", + }); + } + }, + ]); return { name: "php", diff --git a/src/php/artisan.ts b/src/php/artisan.ts index ba590100719c..1f2e6c2ee428 100644 --- a/src/php/artisan.ts +++ b/src/php/artisan.ts @@ -1,12 +1,15 @@ const completionSpec: Fig.Spec = { name: "artisan", description: "Laravel Artisan Command", - generateSpec: async (tokens, executeShellCommand) => { - var out = await executeShellCommand("php artisan list --format=json"); + generateSpec: async (_, executeShellCommand) => { + var { stdout } = await executeShellCommand({ + command: "php", + args: ["artisan", "list", "--format=json"], + }); const subcommands = []; try { - const commandDefinition = JSON.parse(out); + const commandDefinition = JSON.parse(stdout); commandDefinition.commands.map((command) => { subcommands.push({ diff --git a/src/php/bin-console.ts b/src/php/bin-console.ts index 1eb7238c2eff..03feb0e69976 100644 --- a/src/php/bin-console.ts +++ b/src/php/bin-console.ts @@ -36,11 +36,14 @@ const completionSpec: Fig.Spec = { name: "bin-console", description: "Symfony bin/console command", generateSpec: async (_, executeShellCommand) => { - const out = await executeShellCommand("php bin/console list --format=json"); + const { stdout } = await executeShellCommand({ + command: "php", + args: ["bin/console", "list", "--format=json"], + }); let subcommands = []; try { - const commandDefinitions = JSON.parse(out) as BinConsoleJSON; + const commandDefinitions = JSON.parse(stdout) as BinConsoleJSON; subcommands = commandDefinitions.commands.map((command) => ({ name: command.name, diff --git a/src/php/please.ts b/src/php/please.ts index 79000ec4af24..1f4dad98c3a3 100644 --- a/src/php/please.ts +++ b/src/php/please.ts @@ -20,11 +20,14 @@ const completionSpec: Fig.Spec = { name: "please", description: "Statamic Please command", generateSpec: async (tokens, executeShellCommand) => { - const out = await executeShellCommand("php please list --format=json"); + const { stdout } = await executeShellCommand({ + command: "php", + args: ["please", "list", "--format=json"], + }); const subcommands = []; try { - const commandDefinition = JSON.parse(out); + const commandDefinition = JSON.parse(stdout); commandDefinition.commands.map((command) => { subcommands.push({ diff --git a/src/phpunit-watcher.ts b/src/phpunit-watcher.ts index 1ec677ca40cd..84973160ee21 100644 --- a/src/phpunit-watcher.ts +++ b/src/phpunit-watcher.ts @@ -1,5 +1,5 @@ const tests: Fig.Generator = { - script: "phpunit --list-tests", + script: ["phpunit", "--list-tests"], postProcess: function (out) { if (out.startsWith("fatal:")) { return []; diff --git a/src/pip.ts b/src/pip.ts index 6824199812cd..e70048c2e485 100644 --- a/src/pip.ts +++ b/src/pip.ts @@ -1,5 +1,5 @@ const listPackages: Fig.Generator = { - script: "pip list", + script: ["pip", "list"], postProcess: function (out) { const lines = out.split("\n"); const packages = []; diff --git a/src/pipx.ts b/src/pipx.ts index a11437a07763..20ae187e1156 100644 --- a/src/pipx.ts +++ b/src/pipx.ts @@ -1,5 +1,5 @@ const packagesGenerator: Fig.Generator = { - script: "pipx list --short", + script: ["pipx", "list", "--short"], postProcess: (out) => { return out.split("\n").map((line) => { return { diff --git a/src/pkgutil.ts b/src/pkgutil.ts index 38420e213940..52cc1be98228 100644 --- a/src/pkgutil.ts +++ b/src/pkgutil.ts @@ -25,7 +25,7 @@ const postProcessPkgFilenames = export const pkgutilGenerators: Record = { // BOM files bom: { - script: "find . -type f -name '*.bom' -maxdepth 1", + script: ["find", ".", "-type", "f", "-name", "*.bom", "-maxdepth", "1"], postProcess: function (out) { return out.split("\n").map((filepath) => ({ name: filepath.replace("./", ""), @@ -34,12 +34,12 @@ export const pkgutilGenerators: Record = { }, // Installed package ids packageIds: { - script: "pkgutil --pkgs", + script: ["pkgutil", "--pkgs"], splitOn: "\n", }, // .pkg files pkgs: { - script: "find . -type f -name '*.pkg' -maxdepth 1", + script: ["find", ".", "-type", "f", "-name", "*.pkg", "-maxdepth", "1"], postProcess: function (out) { return out.split("\n").map((filepath) => ({ name: filepath.replace("./", ""), @@ -48,7 +48,7 @@ export const pkgutilGenerators: Record = { }, // group ids groupIds: { - script: "pkgutil --groups", + script: ["pkgutil", "--groups"], splitOn: "\n", }, // filenames within a package @@ -63,7 +63,15 @@ export const pkgutilGenerators: Record = { const pkgId = tokens[pkgIdIndex]; const pathPrefix = tokens[tokens.length - 1]; const pp = postProcessPkgFilenames({ pathPrefix }); - return pp(await executeShellCommand(`pkgutil --files ${pkgId}`), tokens); + return pp( + ( + await executeShellCommand({ + command: "pkgutil", + args: ["--files", pkgId], + }) + ).stdout, + tokens + ); }, trigger: "/", }, diff --git a/src/pnpm.ts b/src/pnpm.ts index 16147931c155..8d57d54e39c6 100644 --- a/src/pnpm.ts +++ b/src/pnpm.ts @@ -10,7 +10,7 @@ const filterMessages = (out: string): string => { }; const searchBranches: Fig.Generator = { - script: "git branch --no-color", + script: ["git", "branch", "--no-color"], postProcess: function (out) { const output = filterMessages(out); @@ -45,7 +45,7 @@ const searchBranches: Fig.Generator = { }; const generatorInstalledPackages: Fig.Generator = { - script: "pnpm ls", + script: ["pnpm", "ls"], postProcess: function (out) { /** * out diff --git a/src/pre-commit.ts b/src/pre-commit.ts index 2afd9cf7933c..bfaabcfc3ce3 100644 --- a/src/pre-commit.ts +++ b/src/pre-commit.ts @@ -2,7 +2,7 @@ import YAML from "yaml"; import { gitGenerators } from "./git"; const hooksInConfig: Fig.Generator = { - script: "cat .pre-commit-config.yaml", + script: ["cat",".pre-commit-config.yaml"], postProcess: (output) => { const suggestions: Fig.Suggestion[] = []; diff --git a/src/projj.ts b/src/projj.ts index 0894066cfacb..3dae6e5fa186 100644 --- a/src/projj.ts +++ b/src/projj.ts @@ -1,7 +1,11 @@ const repoGenerator: Fig.Generator = { - script: "cat ~/.projj/cache.json", - postProcess: function (out) { - const cache = JSON.parse(out); + custom: async (_, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${context.environmentVariables["HOME"]}/.projj/cache.json`], + }); + const cache = JSON.parse(stdout); return Object.keys(cache).map((key) => ({ name: key.split("/").pop(), description: cache[key].repo, @@ -10,9 +14,13 @@ const repoGenerator: Fig.Generator = { }; const hookGenerator: Fig.Generator = { - script: "cat ~/.projj/config.json", - postProcess: function (out) { - const cache = JSON.parse(out); + custom: async (_, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${context.environmentVariables["HOME"]}/.projj/config.json`], + }); + const cache = JSON.parse(stdout); const hooks = cache.hooks; return Object.keys(hooks).map((key) => ({ name: key, diff --git a/src/pulumi.ts b/src/pulumi.ts index ed47d4127973..4069341456f6 100644 --- a/src/pulumi.ts +++ b/src/pulumi.ts @@ -2,7 +2,7 @@ const stacksGenerator: Fig.Generator = { cache: { cacheByDirectory: true, }, - script: "pulumi stack ls --json", + script: ["pulumi", "stack", "ls", "--json"], postProcess: (out) => { try { return JSON.parse(out).map((stack) => ({ diff --git a/src/pyenv.ts b/src/pyenv.ts index f0ad760452ee..89032fe75e86 100644 --- a/src/pyenv.ts +++ b/src/pyenv.ts @@ -1,5 +1,5 @@ const versionList: Fig.Generator = { - script: "pyenv install -l", + script: ["pyenv", "install", "-l"], postProcess: function (out) { return out .trim() @@ -10,7 +10,7 @@ const versionList: Fig.Generator = { }; const globalList: Fig.Generator = { - script: "pyenv versions", + script: ["pyenv", "versions"], postProcess: function (out) { return out .trim() diff --git a/src/quickmail.ts b/src/quickmail.ts index 95f8c3b3b044..e3e43085672b 100644 --- a/src/quickmail.ts +++ b/src/quickmail.ts @@ -1,5 +1,5 @@ const bodyTempalates: Fig.Generator = { - script: "quickmail template listall", + script: ["quickmail", "template", "listall"], postProcess: (output) => { const items = output.split("\n"); return items.map((item) => { diff --git a/src/r.ts b/src/r.ts index cf7a61d8d074..4cf252fe25c2 100644 --- a/src/r.ts +++ b/src/r.ts @@ -19,7 +19,7 @@ const RFileGenerator = filepaths({ }); const RLibGenerator: Fig.Generator = { - script: "Rscript -e '.libPaths()'", + script: ["Rscript", "-e", ".libPaths()"], postProcess: (output) => { if (output.includes("not found")) { return []; diff --git a/src/rails.ts b/src/rails.ts index f35895a3b23f..cef2246569e6 100644 --- a/src/rails.ts +++ b/src/rails.ts @@ -483,7 +483,7 @@ const defaultCommands: Fig.Subcommand[] = [ { name: "generator", generators: { - script: "rails g --help", + script: ["rails", "g", "--help"], postProcess(out) { const lines = out.split("Rails:")[1].trim().split("\n"); diff --git a/src/rake.ts b/src/rake.ts index f525172aa0f0..838128c2b569 100644 --- a/src/rake.ts +++ b/src/rake.ts @@ -7,7 +7,7 @@ const completionSpec: Fig.Spec = { isVariadic: true, isOptional: true, generators: { - script: "rake --tasks --silent", + script: ["rake", "--tasks", "--silent"], cache: { strategy: "stale-while-revalidate", cacheByDirectory: true, diff --git a/src/rancher.ts b/src/rancher.ts index 7f3878c981ef..3dfc6a920e19 100644 --- a/src/rancher.ts +++ b/src/rancher.ts @@ -1,7 +1,7 @@ import { filepaths } from "@fig/autocomplete-generators"; const serverList: Fig.Generator = { - script: "rancher server ls", + script: ["rancher", "server", "ls"], postProcess: function (out) { const lines = out.split("\n"); const serversList = []; diff --git a/src/rbenv.ts b/src/rbenv.ts index d09688b902cd..59ea346966f5 100644 --- a/src/rbenv.ts +++ b/src/rbenv.ts @@ -1,12 +1,12 @@ const installVersionsGenerator: Fig.Generator = { - script: "rbenv install -L", + script: ["rbenv", "install", "-L"], postProcess: function (out) { return out.split("\n").map((name) => ({ name })); }, }; const installedVersionsGenerator: Fig.Generator = { - script: "rbenv versions --bare", + script: ["rbenv", "versions", "--bare"], postProcess: function (out) { return out.split("\n").map((name) => ({ name })); }, diff --git a/src/rclone.ts b/src/rclone.ts index 228e3593422f..5cfb345825a3 100644 --- a/src/rclone.ts +++ b/src/rclone.ts @@ -2,7 +2,7 @@ const remote: Fig.Arg = { name: "remote:", generators: { - script: "rclone listremotes", + script: ["rclone", "listremotes"], postProcess: (list) => list.split("\n").map((remote) => ({ name: remote })), }, }; diff --git a/src/react-native.ts b/src/react-native.ts index 08c0db3437a6..997e9b9fc3c0 100644 --- a/src/react-native.ts +++ b/src/react-native.ts @@ -14,7 +14,7 @@ const getJsFilesAndFolders = filepaths({ }); const workerGenerator = { - script: "sysctl -n hw.ncpu", + script: ["sysctl","-n","hw.ncpu"], postProcess: (scriptOutput: string) => { return Array.from({ length: Number(scriptOutput) }, (_x, i) => ({ name: `${i}`, @@ -40,7 +40,7 @@ const xcodeSchemeGenerator = { }; const androidGetDevicesGenerator = { - script: "adb devices", + script: ["adb", "devices"], postProcess: (scriptOutput: string) => { const devices = scriptOutput .split("\n") @@ -58,7 +58,7 @@ const androidGetDevicesGenerator = { type IosRecordType = { name: string }; const iosGetDevicesSimulatorGenerator = { - script: "xcrun simctl list --json devices available", + script: ["xcrun", "simctl", "list", "--json", "devices", "available"], postProcess: (scriptOutput: string) => { const devices = JSON.parse(scriptOutput).devices; @@ -76,7 +76,7 @@ const iosGetDevicesSimulatorGenerator = { }; const iosGetDevicesGenerator = { - script: "xcrun xctrace list devices", + script: ["xcrun", "xctrace", "list", "devices"], postProcess: (scriptOutput: string) => { const devices = scriptOutput .split("\n") diff --git a/src/redwood.ts b/src/redwood.ts index 42d8a246f2e6..21c56ca78d91 100644 --- a/src/redwood.ts +++ b/src/redwood.ts @@ -3,8 +3,11 @@ import prismaSpec from "./prisma"; const icon = "https://avatars.githubusercontent.com/u/45050444?s=200&v=4"; const scripts: Fig.Generator = { - script: + script: [ + "bash", + "-c", "until [[ -f redwood.toml ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1p scripts/", + ], postProcess: (output) => { if (output.trim() === "") { return []; diff --git a/src/rugby.ts b/src/rugby.ts index 83e40e7ec754..cb7bab9d6f1b 100644 --- a/src/rugby.ts +++ b/src/rugby.ts @@ -1,6 +1,6 @@ // Print plans list if there is .rugby/plans.yml file const planList: Fig.Generator = { - script: "rugby plan list", + script: ["rugby", "plan", "list"], postProcess: (output) => { if (output === "") { return []; diff --git a/src/rush.ts b/src/rush.ts index 2a6d75b128a7..dece2fcfd7bf 100644 --- a/src/rush.ts +++ b/src/rush.ts @@ -8,8 +8,11 @@ interface IRushConfigurationJson { projects: IRushConfigurationProjectJson[]; } const projectGenerator: Fig.Generator = { - script: + script: [ + "bash", + "-c", "until [[ -f rush.json ]] || [[ $PWD = '/' ]]; do cd ..; done; cat rush.json", + ], postProcess: function (out: string) { const suggestions = []; diff --git a/src/rustup.ts b/src/rustup.ts index a2eb81c70b3d..78a28aa437fb 100644 --- a/src/rustup.ts +++ b/src/rustup.ts @@ -6,7 +6,7 @@ type ToolchainLocalGeneratorOptions = { const toolchainLocalGenertor: ( args?: ToolchainLocalGeneratorOptions ) => Fig.Generator = ({ excludeShort } = {}) => ({ - script: "rustup toolchain list", + script: ["rustup", "toolchain", "list"], postProcess: (out) => { const toolchains = out .split("\n") @@ -29,12 +29,15 @@ const toolchainLocalGenertor: ( const toolchainAllGenerator: Fig.Generator = { // Grab the latest versions of rust from github, try the gh cli first as it has a higher rate limit - script: + script: [ + "bash", + "-c", 'if command -v gh > /dev/null; then \ gh api -H "Accept: application/vnd.github+json" /repos/rust-lang/rust/releases; \ else \ curl -sfL -H "Accept: application/vnd.github+json" https://api.github.com/repos/rust-lang/rust/releases; \ fi', + ], cache: { // 1 hour, the github api is rate limited per hour ttl: 60 * 60 * 1000, @@ -75,7 +78,7 @@ type TripleGeneratorOptions = { const tripleGenerator: (args?: TripleGeneratorOptions) => Fig.Generator = ({ installed, } = {}) => ({ - script: "rustup target list", + script: ["rustup", "target", "list"], postProcess: (data: string) => { return data .split("\n") @@ -690,7 +693,11 @@ const completionSpec: Fig.Spec = { description: "Topic such as 'core', 'fn', 'usize', 'eprintln!', 'core::arch', 'alloc::format!', 'std::fs', 'std::fs::read_dir', 'std::io::Bytes', 'std::iter::Sum', 'std::io::error::Result' etc", generators: { - script: `find $(rustup docs --path | sed -e "s|index\\.html|std|") $(rustup docs --path | sed -e "s|index\\.html|alloc|") $(rustup docs --path | sed -e "s|index\\.html|core|") | grep "\\.html" | sed -E -e "s|^(.*)/html/||" -e "s|\\.html||" -e "s|/|::|g" -e "s/constant\\.|trait\\.|struct\\.|macro\\.|fn\\.|keyword\\.|primitive\\.|type\\.|enum\\.|union\\.|traitalias\\.|::index$|^(.*)::all$//" -e "/^$/d"`, + script: [ + "bash", + "-c", + `find $(rustup docs --path | sed -e "s|index\\.html|std|") $(rustup docs --path | sed -e "s|index\\.html|alloc|") $(rustup docs --path | sed -e "s|index\\.html|core|") | grep "\\.html" | sed -E -e "s|^(.*)/html/||" -e "s|\\.html||" -e "s|/|::|g" -e "s/constant\\.|trait\\.|struct\\.|macro\\.|fn\\.|keyword\\.|primitive\\.|type\\.|enum\\.|union\\.|traitalias\\.|::index$|^(.*)::all$//" -e "/^$/d"`, + ], splitOn: "\n", }, }, diff --git a/src/scarb.ts b/src/scarb.ts index bb5775f944d5..498c8efbf137 100644 --- a/src/scarb.ts +++ b/src/scarb.ts @@ -29,7 +29,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -105,7 +111,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -145,7 +157,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -313,7 +331,7 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: ["scarb","metadata","--format-version","1","--no-deps"], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -477,7 +495,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -550,7 +574,7 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: "scarb metadata --format-version 1 --no-deps", + script: ["scarb","metadata","--format-version","1","--no-deps"], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; diff --git a/src/scc.ts b/src/scc.ts index 06bfc1da6ea6..3c75f420b27c 100644 --- a/src/scc.ts +++ b/src/scc.ts @@ -36,8 +36,12 @@ const generateLanguages: KeyValueSuggestions = async ( _, executeShellCommand ) => { - const out = await executeShellCommand("scc --language"); - const { languages } = processSccLanguages(out); + const { stdout } = await executeShellCommand({ + command: "scc", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--language"], + }); + const { languages } = processSccLanguages(stdout); return languages.map((language) => ({ name: language })); }; @@ -125,8 +129,12 @@ const completionSpec: Fig.Spec = { cache: true, separator: ":", keys: async (_, executeShellCommand) => { - const out = await executeShellCommand("scc --languages"); - const { extensions } = processSccLanguages(out); + const { stdout } = await executeShellCommand({ + command: "scc", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--language"], + }); + const { extensions } = processSccLanguages(stdout); return Object.entries(extensions).map(([extension, language]) => ({ name: extension, description: language, @@ -178,8 +186,12 @@ const completionSpec: Fig.Spec = { separator: ":", keys: suggestOutputFormats, values: async (_, executeShellCommand) => { - const out = await executeShellCommand("ls -lAF1"); - const suggestions: Fig.Suggestion[] = out + const { stdout } = await executeShellCommand({ + command: "ls", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["-lAF1"], + }); + const suggestions: Fig.Suggestion[] = stdout .split("\n") .map((path) => ({ name: path.slice(path.lastIndexOf("/") + 1), @@ -217,8 +229,12 @@ const completionSpec: Fig.Spec = { generators: valueList({ cache: true, values: async (_, executeShellCommand) => { - const out = await executeShellCommand("scc --languages"); - const { extensions } = processSccLanguages(out); + const { stdout } = await executeShellCommand({ + command: "scc", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--language"], + }); + const { extensions } = processSccLanguages(stdout); return Object.entries(extensions).map(([extension, language]) => ({ name: extension, description: language, diff --git a/src/serverless.ts b/src/serverless.ts index afc0e864b6a5..1507575e1c18 100644 --- a/src/serverless.ts +++ b/src/serverless.ts @@ -404,10 +404,12 @@ const completionSpec: Fig.Spec = { }, ], generateSpec: async (tokens, executeShellCommand) => { - const serverlessCompose = await executeShellCommand( - "cat serverless-compose.yml" - ); - const servicesObject = YAML.parse(serverlessCompose).services; + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["serverless-compose.yml"], + }); + const servicesObject = YAML.parse(stdout).services; const services: string[] = Object.keys(servicesObject); // Avoid infinite recursion of generated subcommands if (services.includes(tokens[0])) { diff --git a/src/shadcn-ui.ts b/src/shadcn-ui.ts index 80d97bb554b7..c2c8bafdbc22 100644 --- a/src/shadcn-ui.ts +++ b/src/shadcn-ui.ts @@ -7,10 +7,14 @@ interface RegistryItem { const componentGenerator: Fig.Generator = { custom: async (_tokens, executeShellCommand) => { - const json = await executeShellCommand( - `curl -sL 'https://raw.githubusercontent.com/shadcn/ui/main/apps/www/public/registry/index.json'` - ); - const components = JSON.parse(json) as RegistryItem[]; + const { stdout } = await executeShellCommand({ + command: "curl", + args: [ + "-sL", + "https://raw.githubusercontent.com/shadcn/ui/main/apps/www/public/registry/index.json", + ], + }); + const components = JSON.parse(stdout) as RegistryItem[]; return components.map((component) => ({ name: component.name, description: component.type, diff --git a/src/shopify/index.ts b/src/shopify/index.ts index 8ad3cd2c8eb5..749b10b9a5b3 100644 --- a/src/shopify/index.ts +++ b/src/shopify/index.ts @@ -4,7 +4,10 @@ export const getVersionCommand: Fig.GetVersionCommand = async ( executeShellCommand ) => { const versionRegex = /\d+\.\d+\.\d+/; - const out = await executeShellCommand("shopify version"); - return out.match(versionRegex)?.[0] ?? ""; + const { stdout } = await executeShellCommand({ + command: "shopify", + args: ["version"], + }); + return stdout.match(versionRegex)?.[0] ?? ""; }; export default createVersionedSpec("shopify", versionFiles); diff --git a/src/shortcuts.ts b/src/shortcuts.ts index b03c44be9796..a73237030b5c 100644 --- a/src/shortcuts.ts +++ b/src/shortcuts.ts @@ -2,7 +2,7 @@ import { filepaths } from "@fig/autocomplete-generators"; const shortcut: Fig.Arg = { generators: { - script: "shortcuts list", + script: ["shortcuts", "list"], postProcess: (list) => list.split("\n").map((shortcut) => ({ name: shortcut, @@ -85,7 +85,7 @@ const subcommands: Fig.Subcommand[] = [ args: { name: "folder-name", generators: { - script: "shortcuts list --folders", + script: ["shortcuts", "list", "--folders"], postProcess: (list) => list.split("\n").map((folder) => ({ name: folder, diff --git a/src/snaplet.ts b/src/snaplet.ts index 4eca5ed10e09..7ff572a1b1e3 100644 --- a/src/snaplet.ts +++ b/src/snaplet.ts @@ -65,7 +65,7 @@ function parsePreviewDatabaseList(output: string): PreviewDatabase[] { } const snapshotsGenerator: Fig.Generator = { - script: "snaplet snapshot ls", + script: ["snaplet", "snapshot", "ls"], postProcess: (output) => { const result: Fig.Suggestion[] = []; const snapshotList = parseSnapshotList(output); @@ -83,7 +83,7 @@ const snapshotsGenerator: Fig.Generator = { // Only suggest successful snapshots on the cloud const snapshotsSuccessCloudGenerator: Fig.Generator = { - script: "snaplet snapshot ls", + script: ["snaplet", "snapshot", "ls"], postProcess: (output) => { const result: Fig.Suggestion[] = []; const snapshotList = parseSnapshotList(output).filter( @@ -102,7 +102,7 @@ const snapshotsSuccessCloudGenerator: Fig.Generator = { }; const databaseGenerator: Fig.Generator = { - script: "snaplet database ls", + script: ["snaplet", "database", "ls"], postProcess: (output) => { const result: Fig.Suggestion[] = []; const databases = parsePreviewDatabaseList(output); diff --git a/src/softwareupdate.ts b/src/softwareupdate.ts index 5d462ca807c8..647507fbb9c5 100644 --- a/src/softwareupdate.ts +++ b/src/softwareupdate.ts @@ -1,5 +1,5 @@ const updatesGenerator: Fig.Generator = { - script: "softwareupdate --list", + script: ["softwareupdate", "--list"], postProcess: (out) => { return out .split("\n") diff --git a/src/spring.ts b/src/spring.ts index 8a6d6973ce1d..44b9fbfb5f09 100644 --- a/src/spring.ts +++ b/src/spring.ts @@ -29,16 +29,23 @@ type Dependency = Option & { const memoizedFetchData = () => { let data: Data | undefined; return async ( - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ): Promise => { if (data) { return data; } try { - const query = `curl -sfL -H "Accept: application/json" "https://start.spring.io/metadata/client"`; - const response = await executeShellCommand(query); - data = JSON.parse(response); + const { stdout } = await executeShellCommand({ + command: "curl", + args: [ + "-sfL", + "-H", + "Accept: application/json", + "https://start.spring.io/metadata/client", + ], + }); + data = JSON.parse(stdout); return data; } catch (error) { return undefined; diff --git a/src/ssh.ts b/src/ssh.ts index 43550e214c4e..7b1be51a30fb 100644 --- a/src/ssh.ts +++ b/src/ssh.ts @@ -1,8 +1,12 @@ const knownHostRegex = /(?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9]+/; // will match numerical IPs as well as domains/subdomains -const resolveAbsolutePath = (path: string, basePath: string): string => { +const resolveAbsolutePath = ( + path: string, + basePath: string, + home: string +): string => { if (path.startsWith("/") || path.startsWith("~/") || path === "~") { - return path; + return path.replace("~", home); } return basePath + (basePath.endsWith("/") ? "" : "/") + path; @@ -10,13 +14,18 @@ const resolveAbsolutePath = (path: string, basePath: string): string => { const getConfigLines = async ( file: string, - executeShellCommand: Fig.ExecuteShellCommandFunction, - basePath + executeShellCommand: Fig.ExecuteCommandFunction, + home: string, + basePath: string ) => { - const absolutePath = resolveAbsolutePath(file, basePath); + const absolutePath = resolveAbsolutePath(file, basePath, home); - const out = await executeShellCommand(`cat ${absolutePath}`); - const configLines = out.split("\n").map((line) => line.trim()); + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [absolutePath], + }); + const configLines = stdout.split("\n").map((line) => line.trim()); // Get list of includes in the config file const includes = configLines @@ -25,7 +34,9 @@ const getConfigLines = async ( // Get the lines of every include file const includeLines = await Promise.all( - includes.map((file) => getConfigLines(file, executeShellCommand, basePath)) + includes.map((file) => + getConfigLines(file, executeShellCommand, home, basePath) + ) ); // Combine config lines with includes config lines @@ -33,9 +44,14 @@ const getConfigLines = async ( }; export const knownHosts: Fig.Generator = { - script: "cat ~/.ssh/known_hosts", - postProcess: function (out, tokens) { - return out + custom: async (tokens, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${context.environmentVariables["HOME"]}/.ssh/known_hosts`], + }); + + return stdout .split("\n") .map((line) => { const match = knownHostRegex.exec(line); @@ -53,10 +69,11 @@ export const knownHosts: Fig.Generator = { }; export const configHosts: Fig.Generator = { - custom: async (tokens, executeShellCommand) => { + custom: async (tokens, executeShellCommand, context) => { const configLines = await getConfigLines( "config", executeShellCommand, + context.environmentVariables["HOME"], "~/.ssh" ); diff --git a/src/stepzen.ts b/src/stepzen.ts index fe5ea8b27473..7d6789beb8a9 100644 --- a/src/stepzen.ts +++ b/src/stepzen.ts @@ -3,7 +3,7 @@ // Coded on stepzen/0.9.33 CLI version const endpointsGenerator: Fig.Generator = { - script: "stepzen list schemas", + script: ["stepzen", "list", "schemas"], postProcess: (output) => { try { return JSON.parse(output).map((endpoint: string) => { @@ -19,7 +19,7 @@ const endpointsGenerator: Fig.Generator = { }; const importSchemasGenerator: Fig.Generator = { - script: "curl https://api.github.com/repos/steprz/stepzen-schemas/contents", + script: ["curl","https://api.github.com/repos/steprz/stepzen-schemas/contents"], postProcess: (output) => { try { return JSON.parse(output) diff --git a/src/svtplay-dl.ts b/src/svtplay-dl.ts index b4ef148a267b..77927226be4e 100644 --- a/src/svtplay-dl.ts +++ b/src/svtplay-dl.ts @@ -1,6 +1,6 @@ const svtplayDlGenerators: Record = { listClipboard: { - script: "pbpaste", + script: ["pbpaste"], postProcess: function (out) { const regex = new RegExp( "^(https?://)?(www.)?(aftonbladet.se|dbtv.no|di.se|dn.se|dr.dk|efn.se|expressen.se|filmarkivet.se|flowonline.tv|nickelodeon.nl|nickelodeon.no|nickelodeon.se|nrk.se|oppetarkiv.se|pokemon.com|ruv.is|riksdagen.se|svd.se|sverigesradio.se|svtplay.se|viafree.se|viafree.no|viafree.dk|tv3play.ee|tv3play.it|tv3play.lv|tv4.se|tv4play.se|twitch.tv|ur.se|urplay.se|vg.no|viagame.com|viasat4play.no|viasatsport.se)/.+$" diff --git a/src/sysctl.ts b/src/sysctl.ts index 88a0f2be950f..d60f34c37d6e 100644 --- a/src/sysctl.ts +++ b/src/sysctl.ts @@ -1,5 +1,5 @@ const generateSysctlNames: Fig.Generator = { - script: "sysctl -A -N", + script: ["sysctl", "-A", "-N"], postProcess: (out) => { return out.split("\n").map((line) => { return { diff --git a/src/systemctl.ts b/src/systemctl.ts index d02dd6aa1f1f..2cd5af02dd0c 100644 --- a/src/systemctl.ts +++ b/src/systemctl.ts @@ -17,10 +17,18 @@ type Unit = { const unitGenerator: Fig.Generator = { custom: async (tokens, executeShellCommand) => { const user = tokens.includes("--user"); - const out = await executeShellCommand( - `systemctl list-units -o json --all --full ${user ? " --user" : ""}` - ); - const units: Unit[] = JSON.parse(out); + const { stdout } = await executeShellCommand({ + command: "systemctl", + args: [ + "list-units", + "-o", + "json", + "--all", + "--full", + ...(user ? ["--user"] : []), + ], + }); + const units: Unit[] = JSON.parse(stdout); const suggustions = units.map((unit) => { let activeEmoji: string; @@ -65,10 +73,18 @@ type UnitFile = { const unitFileGenerator: Fig.Generator = { custom: async (tokens, executeShellCommand) => { const user = tokens.includes("--user"); - const out = await executeShellCommand( - `systemctl list-unit-files -o json --all --full ${user ? " --user" : ""}` - ); - const units: UnitFile[] = JSON.parse(out); + const { stdout } = await executeShellCommand({ + command: "systemctl", + args: [ + "list-unit-files", + "-o", + "json", + "--all", + "--full", + ...(user ? ["--user"] : []), + ], + }); + const units: UnitFile[] = JSON.parse(stdout); const suggustions = units.map((unit) => { let loadedEmoji: string; if (unit.state === "enabled") { diff --git a/src/tailscale.ts b/src/tailscale.ts index c6be5e2f2705..0653d1965d1e 100644 --- a/src/tailscale.ts +++ b/src/tailscale.ts @@ -3,7 +3,7 @@ type HostsGeneratorOptions = { }; const hostsGenerator = ({ append }: HostsGeneratorOptions = {}) => ({ - script: "tailscale status --json", + script: ["tailscale", "status", "--json"], postProcess: (output: string) => Object.values(JSON.parse(output)["Peer"]).map((peer) => ({ name: `${peer["DNSName"].split(".")[0]}${append ?? ""}`, diff --git a/src/task.ts b/src/task.ts index a028f24821bc..57aa910fc0c2 100644 --- a/src/task.ts +++ b/src/task.ts @@ -5,8 +5,12 @@ const completionSpec: Fig.Spec = { name: "task", // loadSpec doesn't work for root commands (https://github.com/withfig/autocomplete/issues/223) generateSpec: async (_tokens, executeShellCommand) => { - const out = await executeShellCommand("task --version"); - return out.includes("Task") ? goTask : taskWarrior; + const { stdout } = await executeShellCommand({ + command: "task", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--version"], + }); + return stdout.includes("Task") ? goTask : taskWarrior; }, }; diff --git a/src/task/go-task.ts b/src/task/go-task.ts index d5ae7c75a8d2..8f5477932a72 100644 --- a/src/task/go-task.ts +++ b/src/task/go-task.ts @@ -3,7 +3,7 @@ const TASKFILE_FLAGS = ["-t", "--taskfile"]; const DIRECTORY_FLAGS = ["-d", "--dir"]; const tasksGenerator: Fig.Generator = { - script: "task -a", + script: ["task", "-a"], postProcess: (output) => { if (output.includes("task: No Taskfile found")) { return []; diff --git a/src/task/taskwarrior.ts b/src/task/taskwarrior.ts index 2a59224c56ec..206e5f20e101 100644 --- a/src/task/taskwarrior.ts +++ b/src/task/taskwarrior.ts @@ -234,9 +234,7 @@ const buildPrioritiesSuggestions = () => { // build the filter list with tasks const filtersWithTasks: Fig.Generator = { - script: function () { - return `task export`; - }, + script: ["task", "export"], postProcess: (output) => { const tasks = JSON.parse(output); const filters = [ @@ -253,9 +251,7 @@ const filtersWithTasks: Fig.Generator = { // build tasks list const listTasks: Fig.Generator = { - script: function (context) { - return `task export`; - }, + script: ["task", "export"], postProcess: (output) => { const tasks = JSON.parse(output); return buildTaskSuggestions(tasks); @@ -264,9 +260,7 @@ const listTasks: Fig.Generator = { // build filter suggestions const filters: Fig.Generator = { - script: function (context) { - return `task export`; - }, + script: ["task", "export"], postProcess: (output) => { const tasks = JSON.parse(output); const filters = [ @@ -282,9 +276,7 @@ const filters: Fig.Generator = { // build modifications suggestions const modifications: Fig.Generator = { - script: function (context) { - return `task export`; - }, + script: ["task", "export"], postProcess: (output) => { const tasks = JSON.parse(output); const filters = [ diff --git a/src/terraform.ts b/src/terraform.ts index 40f1b320bb5d..311ead39cc70 100644 --- a/src/terraform.ts +++ b/src/terraform.ts @@ -1,6 +1,6 @@ // If you edit commands or options, please copy our changes on to the terragrunt spec const workspaceList: Fig.Generator = { - script: "terraform workspace list", + script: ["terraform", "workspace", "list"], postProcess: function (out) { return out.split("\n").map((workspace) => { return { @@ -13,7 +13,7 @@ const workspaceList: Fig.Generator = { }; const addressList: Fig.Generator = { - script: "terraform state list", + script: ["terraform", "state", "list"], postProcess: function (out) { if (out.includes("No state file was found!") || out.includes("Error")) { return []; diff --git a/src/terragrunt.ts b/src/terragrunt.ts index c3e810bd0409..070afea7d957 100644 --- a/src/terragrunt.ts +++ b/src/terragrunt.ts @@ -1,7 +1,7 @@ // If you edit terraform commands or options, please copy our changes on to the terraform spec // ARGUMENTS json-out etc const workspaceList: Fig.Generator = { - script: "terragrunt workspace list", + script: ["terragrunt", "workspace", "list"], postProcess: function (out) { return out.split("\n").map((workspace) => { return { @@ -14,7 +14,7 @@ const workspaceList: Fig.Generator = { }; const addressList: Fig.Generator = { - script: "terragrunt state list", + script: ["terragrunt", "state", "list"], postProcess: function (out) { if (out.includes("No state file was found!") || out.includes("Error")) { return []; diff --git a/src/tfenv.ts b/src/tfenv.ts index 3733116da117..2cd1af765fc0 100644 --- a/src/tfenv.ts +++ b/src/tfenv.ts @@ -1,6 +1,6 @@ const generators: Record = { installedVersions: { - script: "tfenv list", + script: ["tfenv", "list"], postProcess: function (out) { return out.split("\n").map((tfversion) => { return { name: tfversion, description: "Version" }; @@ -8,7 +8,7 @@ const generators: Record = { }, }, allVersions: { - script: "tfenv list-remote", + script: ["tfenv", "list-remote"], postProcess: function (out) { return out.split("\n").map(function (line) { return { name: line, type: "option" }; diff --git a/src/tfsec.ts b/src/tfsec.ts index c8811cd1f19d..22e5ab9895b2 100644 --- a/src/tfsec.ts +++ b/src/tfsec.ts @@ -1,5 +1,5 @@ const workspaceGenerator: Fig.Generator = { - script: "terraform workspace list", + script: ["terraform", "workspace", "list"], postProcess: function (out) { return out.split("\n").map((workspace) => ({ name: workspace.replace("*", "").trim(), diff --git a/src/tmuxinator.ts b/src/tmuxinator.ts index bdd8150d89e1..8ae545984ea8 100644 --- a/src/tmuxinator.ts +++ b/src/tmuxinator.ts @@ -1,5 +1,5 @@ const projects: Fig.Generator = { - script: "tmuxinator list -n", + script: ["tmuxinator", "list", "-n"], postProcess: (output) => { if (output.startsWith("fatal:")) { return []; @@ -17,7 +17,7 @@ const projects: Fig.Generator = { }; const tmuxsessions: Fig.Generator = { - script: "tmux ls", + script: ["tmux", "ls"], postProcess: (output) => { if (output.startsWith("fatal:")) { return []; diff --git a/src/tokei.ts b/src/tokei.ts index 2775cdf8232b..065b0c344c24 100644 --- a/src/tokei.ts +++ b/src/tokei.ts @@ -1,5 +1,5 @@ const languageGenerator: Fig.Generator = { - script: "tokei --languages", + script: ["tokei", "--languages"], splitOn: "\n", }; diff --git a/src/trap.ts b/src/trap.ts index 53a2f4813e74..139132d43209 100644 --- a/src/trap.ts +++ b/src/trap.ts @@ -11,7 +11,7 @@ const re = /(\d+\)\s)?([\w-+]+)/g; const availableSignalsGenerator = ( suggestOptions?: Partial ): Fig.Generator => ({ - script: "command kill -l", + script: ["command", "kill", "-l"], postProcess: (output) => [...output.matchAll(re)].map((signal) => ({ name: signal[2], diff --git a/src/trex.ts b/src/trex.ts index 44bd805926da..8a8b1a648328 100644 --- a/src/trex.ts +++ b/src/trex.ts @@ -1,5 +1,5 @@ const dependenciesGenerator: Fig.Generator = { - script: "cat import_map.json", + script: ["cat","import_map.json"], postProcess: function (out) { if (out) { try { @@ -24,7 +24,7 @@ const dependenciesGenerator: Fig.Generator = { }, }; const scriptsGenerator: Fig.Generator = { - script: "cat run.json", + script: ["cat","run.json"], postProcess: function (out) { if (out) { try { diff --git a/src/tsh.ts b/src/tsh.ts index 42b02b1a5c79..1a801110db3d 100644 --- a/src/tsh.ts +++ b/src/tsh.ts @@ -8,7 +8,7 @@ const globalOptions: Fig.Option[] = [ args: { name: "Teleport proxy address", generators: { - script: "tsh clusters --format=json", + script: ["tsh", "clusters", "--format=json"], postProcess: (out) => JSON.parse(out).map((elm) => elm.cluster_name), }, }, @@ -19,7 +19,7 @@ const globalOptions: Fig.Option[] = [ args: { name: "user", generators: { - script: "tsh status --format json", + script: ["tsh", "status", "--format", "json"], postProcess: (out) => [JSON.parse(out).active.username], }, }, @@ -93,7 +93,7 @@ const completionSpec: Fig.Spec = { name: "user@hostname", description: "Address of remote machine to log into", generators: { - script: "tsh ls --format=json", + script: ["tsh","ls","--format=json"], postProcess: (out) => { return JSON.parse(out).map((elm) => { return { diff --git a/src/tsuru.ts b/src/tsuru.ts index a0fb15233c71..8f01c73a8f6c 100644 --- a/src/tsuru.ts +++ b/src/tsuru.ts @@ -8,7 +8,7 @@ const formatTsuruOutput = (output: string) => { const tsuruGenerators: Record = { plans: { - script: "tsuru plan list", + script: ["tsuru", "plan", "list"], postProcess: function (out) { const plans = formatTsuruOutput(out); return plans.map((plan) => { @@ -18,7 +18,7 @@ const tsuruGenerators: Record = { }, teams: { - script: "tsuru team list", + script: ["tsuru", "team", "list"], postProcess: function (out) { const teams = formatTsuruOutput(out); return teams.map((team) => { @@ -28,7 +28,7 @@ const tsuruGenerators: Record = { }, apps: { - script: "tsuru app list", + script: ["tsuru", "app", "list"], postProcess: function (out) { const apps = formatTsuruOutput(out); return apps.map((app) => { @@ -38,7 +38,7 @@ const tsuruGenerators: Record = { }, platforms: { - script: "tsuru platform list", + script: ["tsuru", "platform", "list"], postProcess: function (out) { return out .split("\n") @@ -50,7 +50,7 @@ const tsuruGenerators: Record = { }, pools: { - script: "tsuru pool list", + script: ["tsuru", "pool", "list"], postProcess: function (out) { const pools = formatTsuruOutput(out); return pools.map((pool) => { diff --git a/src/turbo.ts b/src/turbo.ts index 6c9f9e3524bc..9757208a451d 100644 --- a/src/turbo.ts +++ b/src/turbo.ts @@ -98,8 +98,11 @@ const completionSpec: Fig.Spec = { name: "tasks", isVariadic: true, generators: { - script: + script: [ + "bash", + "-c", "until [[ ( -f turbo.json || $PWD = '/' ) ]]; do cd ..; done; cat turbo.json", + ], postProcess: (out) => { let turbo: TurboJSON; try { diff --git a/src/unset.ts b/src/unset.ts index 2f0520f940da..ef53a673a3a7 100644 --- a/src/unset.ts +++ b/src/unset.ts @@ -1,5 +1,5 @@ const environmentVariableGenerator: Fig.Generator = { - script: "env", + script: ["env"], postProcess: (out) => out.length === 0 ? [] diff --git a/src/v.ts b/src/v.ts index 15c2b1d9121c..4fabb6bc5365 100644 --- a/src/v.ts +++ b/src/v.ts @@ -100,7 +100,7 @@ const completionSpec: Fig.Spec = { description: "Display help for V", args: { generators: { - script: "v help topics", + script: ["v", "help", "topics"], postProcess: (out) => { return out .trim() diff --git a/src/valet.ts b/src/valet.ts index 40da10f2bcb1..01c8dd2208c0 100644 --- a/src/valet.ts +++ b/src/valet.ts @@ -161,7 +161,7 @@ const completionSpec: Fig.Spec = { args: { name: "command", generators: { - script: "valet list --raw", + script: ["valet", "list", "--raw"], postProcess: function (out) { return out.split("\n").map((command) => { const name = command.split(" ")[0]; diff --git a/src/vercel.ts b/src/vercel.ts index 8437c6f14e5d..b18a8635b337 100644 --- a/src/vercel.ts +++ b/src/vercel.ts @@ -1,5 +1,5 @@ const envVarList: Fig.Generator = { - script: "vercel env ls", + script: ["vercel", "env", "ls"], postProcess: function (out) { const lines = out.split("\n"); const envList = []; @@ -13,26 +13,8 @@ const envVarList: Fig.Generator = { }, }; -//Unfinished -const deploymentList: Fig.Generator = { - //Grabs all the deployments for - script: "vercel list [project name]", - postProcess: function (out) { - const lines = out.split("\n"); - const deploymentUrlList = []; - for (let i = 4; i < lines.length; i++) { - const deploymentUrl = lines[i].match(/\S+/g)[1]; - deploymentUrlList.push({ - name: deploymentUrl, - icon: "🔗", - }); - } - return deploymentUrlList; - }, -}; - const domainList = { - script: "vercel domains", + script: ["vercel", "domains"], postProcess: function (out) { const lines = out.split("\n"); const domainList = []; @@ -47,7 +29,7 @@ const domainList = { }; const teamList: Fig.Generator = { - script: "vercel teams list", + script: ["vercel", "teams", "list"], postProcess: function (out) { const lines = out.split("\n"); const teamList = []; diff --git a/src/vr.ts b/src/vr.ts index 7ab1a16de61a..617ce10ab5cc 100644 --- a/src/vr.ts +++ b/src/vr.ts @@ -4,7 +4,14 @@ const scriptGenerator: Fig.Generator = { cache: { strategy: "stale-while-revalidate", }, - script: "NO_COLOR=1 vr", + script: { + command: "vr", + // eslint-disable-next-line @withfig/fig-linter/no-empty-array-values + args: [], + env: { + NO_COLOR: "1", + }, + }, postProcess: (out) => { const suggestions: Fig.Suggestion[] = []; diff --git a/src/vsce.ts b/src/vsce.ts index 5097aff4e68d..3e69a96d0457 100644 --- a/src/vsce.ts +++ b/src/vsce.ts @@ -17,7 +17,7 @@ const targetSuggestions: string[] = [ // NOTE: Running `vsce ls-publishers` requires access to keychain of `vscode-vsce` // which distracts the completion and needs to "Allow always" the access of it to work well. // const publishersGenerator: Fig.Generator = { -// script: "vsce ls-publishers", +// script: ["vsce", "ls-publishers"], // postProcess: (out) => { // if (out.trim() === "") return []; // return out.split("\n").map((publisher) => ({ diff --git a/src/vultr-cli.ts b/src/vultr-cli.ts index 41dd0ce4b4f2..9e69ae2f5871 100644 --- a/src/vultr-cli.ts +++ b/src/vultr-cli.ts @@ -9,7 +9,7 @@ const backupIdArg: Fig.Arg = { const instance: Fig.Arg = { name: "instanceID", generators: { - script: "vultr-cli instance list", + script: ["vultr-cli", "instance", "list"], postProcess: (lines) => lines .split("\n") diff --git a/src/watson.ts b/src/watson.ts index 632c64e0d59b..bb6ff8575de7 100644 --- a/src/watson.ts +++ b/src/watson.ts @@ -1,6 +1,6 @@ // https://tailordev.github.io/Watson/ const listProjects: Fig.Generator = { - script: `watson projects`, + script: ["watson", "projects"], postProcess: (output) => { return output.split("\n").map((project) => ({ name: project, @@ -10,7 +10,7 @@ const listProjects: Fig.Generator = { }; const listTags: Fig.Generator = { - script: `watson tags`, + script: ["watson", "tags"], postProcess: (output) => { return output.split("\n").map((tag) => ({ name: tag, @@ -20,7 +20,7 @@ const listTags: Fig.Generator = { }; const listFrames: Fig.Generator = { - script: `watson log --json --reverse`, + script: ["watson", "log", "--json", "--reverse"], postProcess: (output) => { return JSON.parse(output).map((frame) => ({ name: frame.id.substring(0, 7), diff --git a/src/wd.ts b/src/wd.ts index 377763c23d77..0c1cd379a775 100644 --- a/src/wd.ts +++ b/src/wd.ts @@ -1,8 +1,13 @@ const warpPointsGenerator: Fig.Generator = { - script: "cat ~/.warprc", - postProcess: (out) => { + custom: async (_, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${context.environmentVariables["HOME"]}/.warprc`], + }); + // find all warp points names - const iter = out.matchAll(/^(\w+)/gm); + const iter = stdout.matchAll(/^(\w+)/gm); const suggestions: Fig.Suggestion[] = []; diff --git a/src/xc.ts b/src/xc.ts index f1cceb840ffd..4f04ca5186eb 100644 --- a/src/xc.ts +++ b/src/xc.ts @@ -30,8 +30,12 @@ const completionSpec: Fig.Spec = { description: 'Specify the heading for xc tasks (default: "Tasks")', }, ]; - const out = await executeShellCommand("xc"); - const subcommands: Fig.Subcommand[] = out + const { stdout } = await executeShellCommand({ + command: "xc", + // eslint-disable-next-line @withfig/fig-linter/no-empty-array-values + args: [], + }); + const subcommands: Fig.Subcommand[] = stdout .trim() .split("\n") .map((line) => diff --git a/src/xcodes.ts b/src/xcodes.ts index 40e9147b6912..9cade074a320 100644 --- a/src/xcodes.ts +++ b/src/xcodes.ts @@ -15,12 +15,12 @@ const processXcodeList = (out: string, tokens: string[]) => })); const allXcodes: Fig.Generator = { - script: "xcodes list", + script: ["xcodes", "list"], postProcess: processXcodeList, }; const installedXcodes: Fig.Generator = { - script: "xcodes installed", + script: ["xcodes", "installed"], postProcess: processXcodeList, }; diff --git a/src/yalc.ts b/src/yalc.ts index 0bb01c9a1594..796957a15ca4 100644 --- a/src/yalc.ts +++ b/src/yalc.ts @@ -21,7 +21,7 @@ const generatePackages: Fig.Generator = { }; const getRemovablePackages: Fig.Generator = { - script: "command ls .yalc", + script: ["ls", ".yalc"], postProcess: (out) => out.split("\n").map((path) => ({ name: path, diff --git a/src/yarn.ts b/src/yarn.ts index fba8c9adea83..81eef2be0947 100644 --- a/src/yarn.ts +++ b/src/yarn.ts @@ -2,8 +2,20 @@ import { npmScriptsGenerator, npmSearchGenerator } from "./npm"; export const yarnScriptParserDirectives: Fig.Arg["parserDirectives"] = { alias: async (token, executeShellCommand) => { - const out = await executeShellCommand("cat $(npm prefix)/package.json"); - const script: string = JSON.parse(out).scripts?.[token]; + const npmPrefix = await executeShellCommand({ + command: "npm", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["prefix"], + }); + if (npmPrefix.status !== 0) { + throw new Error("npm prefix command failed"); + } + const packageJson = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${npmPrefix.stdout.trim()}/package.json`], + }); + const script: string = JSON.parse(packageJson.stdout).scripts?.[token]; if (!script) { throw new Error(`Script not found: '${token}'`); } @@ -68,7 +80,7 @@ const getGlobalPackagesGenerator: Fig.Generator = { // generate package list of direct and indirect dependencies const allDependenciesGenerator: Fig.Generator = { - script: "yarn list --depth=0 --json", + script: ["yarn", "list", "--depth=0", "--json"], postProcess: (out) => { if (out.trim() == "") return []; @@ -85,7 +97,7 @@ const allDependenciesGenerator: Fig.Generator = { }; const configList: Fig.Generator = { - script: "yarn config list", + script: ["yarn", "config", "list"], postProcess: function (out) { if (out.trim() == "") { return []; @@ -112,8 +124,11 @@ const configList: Fig.Generator = { }; export const dependenciesGenerator: Fig.Generator = { - script: + script: [ + "bash", + "-c", "until [[ -f package.json ]] || [[ $PWD = '/' ]]; do cd ..; done; cat package.json", + ], postProcess: function (out, context = []) { if (out.trim() === "") { return []; diff --git a/src/ykman.ts b/src/ykman.ts index 4bbdb0748f1d..4c32e821a0c2 100644 --- a/src/ykman.ts +++ b/src/ykman.ts @@ -2456,7 +2456,7 @@ const completionSpec: Fig.Spec = { args: { name: "NAME", generators: { - script: "ykman list --readers", + script: ["ykman", "list", "--readers"], postProcess: function (out) { return out.split("\n").map((readerName) => { return { name: readerName, description: "Yubikey name" }; diff --git a/src/yo.ts b/src/yo.ts index 1ce389ab6b4f..0e905eb40af8 100644 --- a/src/yo.ts +++ b/src/yo.ts @@ -22,7 +22,7 @@ const suggestions: Fig.Suggestion[] = [ // GENERATORS const yeomanGeneratorList: Fig.Generator = { - script: "yo --generators", + script: ["yo", "--generators"], postProcess: function (out) { try { return out diff --git a/src/youtube-dl.ts b/src/youtube-dl.ts index 7518168d91f1..f8427a349f6c 100644 --- a/src/youtube-dl.ts +++ b/src/youtube-dl.ts @@ -1,9 +1,11 @@ const youtubeDlGenerators: Record = { listVideos: { - script: (context) => - `youtube-dl --flat-playlist -J ${context.filter((token) => - token.includes("youtube.") - )}`, + script: (context) => [ + "youtube-dl", + "--flat-playlist", + "-J", + ...context.filter((token) => token.includes("youtube.")), + ], postProcess: function (out) { try { @@ -23,7 +25,7 @@ const youtubeDlGenerators: Record = { }, listClipboard: { - script: "pbpaste", + script: ["pbpaste"], postProcess: function (out) { const regex = new RegExp( "^(https?://)?(www.)?(youtube.com|youtu.?be)/.+$" diff --git a/src/z.ts b/src/z.ts index 7115c965768d..4487a35f8bfa 100644 --- a/src/z.ts +++ b/src/z.ts @@ -6,10 +6,13 @@ interface ZSuggestion { } async function getZHistory( - execute: Fig.ExecuteShellCommandFunction + execute: Fig.ExecuteCommandFunction ): Promise { - const out = await execute("cat ${${ZSHZ_DATA:-${_Z_DATA:-${HOME}/.z}}:A}"); - return out.split("\n").map((line) => { + const { stdout } = await execute({ + command: "zsh", + args: ["-c", "cat ${${ZSHZ_DATA:-${_Z_DATA:-${HOME}/.z}}:A}"], + }); + return stdout.split("\n").map((line) => { const [path, weight, time] = line.split("|"); const splitPath = path.split("/"); const name = splitPath[splitPath.length - 1]; @@ -122,18 +125,23 @@ const zoxideCompletionSpec: Fig.Spec = { isVariadic: true, generators: { custom: async (tokens, executeShellCommand) => { - console.log(tokens); - let command; + let args; if (tokens.length < 2 || tokens[1] === "") { - command = "zoxide query --list --score"; + args = ["query", "--list", "--score"]; } else { - command = `zoxide query --list --score -- ${tokens - .slice(1) - .join(" ")}`; + args = [ + "query", + "--list", + "--score", + "--", + tokens.slice(1).join(" "), + ]; } - console.log(command); - const out = await executeShellCommand(command); + const { stdout } = await executeShellCommand({ + command: "zoxide", + args, + }); return out.split("\n").map((line) => { const trimmedLine = line.trim(); @@ -158,8 +166,11 @@ const zCompletionSpec: Fig.Spec = { generateSpec: async (_, executeShellCommand) => { // Assume if zoxide is installed, use that completion spec try { - const zoxideInstalled = await executeShellCommand("command -v zoxide"); - if (zoxideInstalled.length > 0) { + const { status } = await executeShellCommand({ + command: "bash", + args: ["-c", "command -v zoxide"], + }); + if (status === 0) { return zoxideCompletionSpec; } } catch (_) {} diff --git a/src/zellij.ts b/src/zellij.ts index 450465cb1fb3..6594774b3115 100644 --- a/src/zellij.ts +++ b/src/zellij.ts @@ -1,5 +1,5 @@ const generateSessions: Fig.Generator = { - script: "zellij list-sessions", + script: ["zellij", "list-sessions"], splitOn: "\n", }; diff --git a/yarn.lock b/yarn.lock index 4fa47b118811..6b28fdf5f5e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -732,10 +732,10 @@ __metadata: languageName: node linkType: hard -"@fig/autocomplete-generators@npm:^2.2.5": - version: 2.2.5 - resolution: "@fig/autocomplete-generators@npm:2.2.5" - checksum: 6744eef510dd4c11ce20fcf8285eb173ae2a5f185c4c04a002e24bb0766e036af30938d07df2f52d3756973e86c5bbc1e363c9a71f91c5b507349f2ed7e3c9a7 +"@fig/autocomplete-generators@npm:^2.3.0": + version: 2.3.0 + resolution: "@fig/autocomplete-generators@npm:2.3.0" + checksum: d2b6be2972c59fc9ea9828c84882650bea4b457dc8abe96b9a127458d923c04411f8a242a8b5e75f53ca2011503610fc8141b1b7ad8bea016f31290aa4685f67 languageName: node linkType: hard @@ -1873,10 +1873,10 @@ __metadata: languageName: node linkType: hard -"@withfig/autocomplete-types@npm:^1.28.0": - version: 1.28.0 - resolution: "@withfig/autocomplete-types@npm:1.28.0" - checksum: ca69f69c4299356b5ef5c1c5c88ac8244773b4a600e616227b7c63c8dc4fe6e5ddb6a6c72d6d70e9296bc870462930b53c28efacb1b6d78e14876a5720527226 +"@withfig/autocomplete-types@npm:^1.29.0": + version: 1.29.0 + resolution: "@withfig/autocomplete-types@npm:1.29.0" + checksum: 714b960c1e4a464e652fa72722d5927cb677f27a0528f5545bb4ebceab885c7035bc89f261450677356708b9455ad802a395d262909475983b64c59229f991e5 languageName: node linkType: hard @@ -1884,7 +1884,7 @@ __metadata: version: 0.0.0-use.local resolution: "@withfig/autocomplete@workspace:." dependencies: - "@fig/autocomplete-generators": ^2.2.5 + "@fig/autocomplete-generators": ^2.3.0 "@fig/autocomplete-helpers": ^1.0.7 "@fig/autocomplete-hooks": ^1.0.2 "@fig/eslint-config-autocomplete": ^1.2.1 @@ -1897,7 +1897,7 @@ __metadata: "@vitejs/plugin-react": ^4.1.0 "@withfig/api-bindings": ^0.30.3 "@withfig/autocomplete-tools": ^2.7.10 - "@withfig/autocomplete-types": ^1.28.0 + "@withfig/autocomplete-types": ^1.29.0 autoprefixer: ^10.4.16 chalk: ^5.3.0 chokidar: ^3.5.3 From af062eed02a7b317e5f4b7cd2e2988562460b576 Mon Sep 17 00:00:00 2001 From: Grant G Date: Mon, 6 Nov 2023 15:10:11 -0800 Subject: [PATCH 2/3] Update types 2 (#2162) --- src/ant.ts | 2 +- src/apt.ts | 13 ++++-- src/aws/cloudwatch.ts | 88 +++++++++++++++++++++++---------------- src/aws/eks.ts | 22 ++++++---- src/aws/iam.ts | 42 ++++++++++++------- src/aws/s3.ts | 24 +++++------ src/aws/secretsmanager.ts | 27 ++++++------ src/bat.ts | 30 ++++++++++--- src/black.ts | 2 +- src/brew.ts | 12 ++++-- src/browser-sync.ts | 5 ++- src/bunx.ts | 6 ++- src/cargo.ts | 76 +++++++++++++++++---------------- src/cf.ts | 4 +- src/chown.ts | 19 ++++++--- src/coda.ts | 6 ++- src/conda.ts | 2 +- src/copilot.ts | 2 +- src/cordova.ts | 2 +- src/deno/generators.ts | 25 +++++++---- src/deployctl.ts | 6 ++- src/docker-compose.ts | 10 ++--- src/doppler.ts | 2 +- src/dotnet/dotnet-run.ts | 2 +- src/dotnet/dotnet-tool.ts | 4 +- src/drush.ts | 6 ++- src/dscl.ts | 6 ++- src/esbuild.ts | 12 ++++-- src/eslint.ts | 5 ++- src/example/trigger.ts | 8 ++-- src/expo-cli.ts | 4 +- src/ffmpeg.ts | 4 +- src/fig/shared.ts | 47 ++++++++------------- src/fin.ts | 12 ++++-- src/firebase.ts | 2 +- src/fisher.ts | 2 +- src/git.ts | 17 ++++++-- src/goto.ts | 10 +++-- src/hexo.ts | 2 +- src/hugo.ts | 2 +- src/iconv.ts | 2 +- src/id.ts | 2 +- src/ignite-cli.ts | 2 +- src/jenv.ts | 8 +++- src/kill.ts | 2 +- src/killall.ts | 4 +- src/kubectx.ts | 2 +- src/kubens.ts | 2 +- src/login.ts | 2 +- src/m.ts | 15 ++++--- src/magento.ts | 8 ++-- src/make.ts | 19 ++++++--- src/mask.ts | 20 ++++++--- src/mkinitcpio.ts | 2 +- src/multipass.ts | 10 ++--- src/nextflow.ts | 18 +++++--- src/node.ts | 11 ++++- src/npm.ts | 69 ++++++++++++++++++++++-------- src/nx.ts | 51 ++++++++++++++++------- src/passwd.ts | 2 +- src/pnpm.ts | 11 ++++- src/pre-commit.ts | 2 +- src/python.ts | 11 +++-- src/python3.ts | 10 +++-- src/rails.ts | 24 ++++++++--- src/react-native.ts | 12 +++--- src/robot.ts | 22 +++++++--- src/rugby.ts | 9 ++-- src/rustup.ts | 8 ++-- src/scarb.ts | 16 ++++++- src/shopify/index.ts | 1 + src/stepzen.ts | 5 ++- src/tldr.ts | 30 ++++++------- src/tmux.ts | 2 +- src/trex.ts | 4 +- src/tsh.ts | 2 +- src/vite.ts | 10 ++++- src/which.ts | 6 ++- src/wifi-password.ts | 5 ++- src/yalc.ts | 6 ++- src/yarn.ts | 69 ++++++++++++++++++++++-------- src/ykman.ts | 6 ++- src/youtube-dl.ts | 33 +++++++++------ src/z.ts | 11 +++-- 84 files changed, 738 insertions(+), 400 deletions(-) diff --git a/src/ant.ts b/src/ant.ts index f8976b5c909e..99e72de90bc9 100644 --- a/src/ant.ts +++ b/src/ant.ts @@ -1,5 +1,5 @@ const tasksGenerator: Fig.Generator = { - script: "command ant -p | grep -i '^\\s' | tr -d ' '", + script: ["bash", "-c", "command ant -p | grep -i '^\\s' | tr -d ' '"], postProcess: (out) => out.split("\n").map((task) => ({ name: task, diff --git a/src/apt.ts b/src/apt.ts index d297075f3ebc..9af3741ca287 100644 --- a/src/apt.ts +++ b/src/apt.ts @@ -11,13 +11,18 @@ const packages: Fig.Generator = { if (finalToken.length === 0) { return []; } + const { stdout } = await executeShellCommand({ + command: "apt", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["list"], + }); + // Only lines matching the first character, delete characters after '/' - const out = await executeShellCommand( - `apt list | grep '^${finalToken[0]}' | sed 's#/.*##g'` - ); - return out + return stdout .trim() .split("\n") + .filter((name) => name.startsWith(finalToken)) + .map((name) => name.replace(/\/.*/, "")) .map((name) => ({ name, description: "Package", diff --git a/src/aws/cloudwatch.ts b/src/aws/cloudwatch.ts index e5274499ccc0..dbf8102022d8 100644 --- a/src/aws/cloudwatch.ts +++ b/src/aws/cloudwatch.ts @@ -177,14 +177,14 @@ const postPrecessGenerator = ( const listCustomGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws cloudwatch ${command}`; + let args = ["cloudwatch", command]; for (let i = 0; i < options.length; i++) { const option = options[i]; @@ -193,12 +193,15 @@ const listCustomGenerator = async ( continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -209,15 +212,13 @@ const listCustomGenerator = async ( ]; } - return list - .map((elm) => { - const name = (childKey ? elm[childKey] : elm) as string; - return { - name, - icon: "fig://icon?type=aws", - }; - }) - .filter(uniqueNames); + return list.map((elm) => { + const name = (childKey ? elm[childKey] : elm) as string; + return { + name, + icon: "fig://icon?type=aws", + }; + }); } catch (e) { console.log(e); } @@ -226,7 +227,7 @@ const listCustomGenerator = async ( const listDimensionTypes = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, option: string, parentKey: string, @@ -237,11 +238,13 @@ const listDimensionTypes = async ( if (idx < 0) { return []; } - const cmd = `aws cloudwatch ${command} ${option} ${tokens[idx + 1]}`; - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["cloudwatch", command, option, tokens[idx + 1]], + }); - const metrics = JSON.parse(out)[parentKey]; + const metrics = JSON.parse(stdout)[parentKey]; // traverse JSON & compose key-value style suggestion return metrics @@ -265,22 +268,29 @@ const listDimensionTypes = async ( const MultiSuggestionsGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - enabled: Record[] + executeShellCommand: Fig.ExecuteCommandFunction, + enabled: { + command: string[]; + parentKey: string; + childKey: string; + }[] ) => { try { const list: Fig.Suggestion[][] = []; const promises: Promise[] = []; for (let i = 0; i < enabled.length; i++) { - promises[i] = executeShellCommand(enabled[i]["command"]); + promises[i] = executeShellCommand({ + command: "aws", + args: enabled[i].command, + }).then(({ stdout }) => stdout); } const result = await Promise.all(promises); for (let i = 0; i < enabled.length; i++) { list[i] = postPrecessGenerator( result[i], - enabled[i]["parentKey"], - enabled[i]["childKey"] + enabled[i].parentKey, + enabled[i].childKey ); } @@ -293,12 +303,15 @@ const MultiSuggestionsGenerator = async ( const getResultList = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, - command: string, + executeShellCommand: Fig.ExecuteCommandFunction, + args: string[], key: string ): Promise => { - const out = await executeShellCommand(command); - return JSON.parse(out)[key]; + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); + return JSON.parse(stdout)[key]; }; const _prefixFile = "file://"; @@ -535,19 +548,22 @@ const generators: Record = { // individually, to get an ARN custom: async function (tokens, executeShellCommand) { // get list of stream names - const result = await Promise.all([ - getResultList( - tokens, - executeShellCommand, - "aws firehose list-delivery-streams", - "DeliveryStreamNames" - ), - ]); + const result = await getResultList( + tokens, + executeShellCommand, + ["firehose", "list-delivery-streams"], + "DeliveryStreamNames" + ); // construct "query" const objects = result.flat().map((stream) => { return { - command: `aws firehose describe-delivery-stream --delivery-stream-name ${stream}`, + command: [ + "firehose", + "describe-delivery-stream", + "--delivery-stream-name", + Array.isArray(stream.name) ? stream.name[0] : stream.name, + ], parentKey: "DeliveryStreamDescription", childKey: "DeliveryStreamARN", }; diff --git a/src/aws/eks.ts b/src/aws/eks.ts index 7cab444f6666..4ccc050d96a8 100644 --- a/src/aws/eks.ts +++ b/src/aws/eks.ts @@ -32,14 +32,14 @@ const postPrecessGenerator = ( const listCustomGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, options: string[], parentKey: string, childKey = "" ): Promise => { try { - let cmd = `aws eks ${command}`; + let args = ["eks", command]; for (let i = 0; i < options.length; i++) { const option = options[i]; @@ -48,12 +48,15 @@ const listCustomGenerator = async ( continue; } const param = tokens[idx + 1]; - cmd += ` ${option} ${param}`; + args = [...args, option, param]; } - const out = await executeShellCommand(cmd); + const { stdout } = await executeShellCommand({ + command: "aws", + args, + }); - const list = JSON.parse(out)[parentKey]; + const list = JSON.parse(stdout)[parentKey]; if (!Array.isArray(list)) { return [ @@ -313,10 +316,11 @@ const generators: Record = { } const param = tokens[idx + 1]; - const out = await executeShellCommand( - `aws eks describe-cluster --name ${param}` - ); - const cluster = JSON.parse(out)["cluster"]; + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["eks", "describe-cluster", "--name", param], + }); + const cluster = JSON.parse(stdout)["cluster"]; const subnets = cluster["resourcesVpcConfig"]["subnetIds"]; return subnets.map((subnet) => { return { diff --git a/src/aws/iam.ts b/src/aws/iam.ts index 2d36c6f2107e..cbaea79063b8 100644 --- a/src/aws/iam.ts +++ b/src/aws/iam.ts @@ -182,15 +182,15 @@ const awsPrincipals = [ ]; interface Identity { - command: string; + command: string[]; parentKey: string; childKey: string; } const identityStruct: Identity[] = [ - { command: "aws iam list-users", parentKey: "Users", childKey: "Arn" }, - { command: "aws iam list-groups", parentKey: "Groups", childKey: "Arn" }, - { command: "aws iam list-roles", parentKey: "Roles", childKey: "Arn" }, + { command: ["iam", "list-users"], parentKey: "Users", childKey: "Arn" }, + { command: ["iam", "list-groups"], parentKey: "Groups", childKey: "Arn" }, + { command: ["iam", "list-roles"], parentKey: "Roles", childKey: "Arn" }, ]; const _prefixFile = "file://"; @@ -288,7 +288,7 @@ const filterWithPrefix = (token: string, prefix: string): string => { const listCustomGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, command: string, option: string, parentKey: string, @@ -300,11 +300,12 @@ const listCustomGenerator = async ( return []; } const param = tokens[idx + 1]; - const out = await executeShellCommand( - `aws iam ${command} ${option} ${param}` - ); + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["iam", command, option, param], + }); - const policies = JSON.parse(out)[parentKey]; + const policies = JSON.parse(stdout)[parentKey]; return policies.map((elm) => (childKey ? elm[childKey] : elm)); } catch (e) { console.log(e); @@ -328,14 +329,17 @@ const postPrecessGenerator = ( const MultiSuggestionsGenerator = async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, enabled: Identity[] ) => { try { const list: Fig.Suggestion[][] = []; const promises: Promise[] = []; for (let i = 0; i < enabled.length; i++) { - promises[i] = executeShellCommand(enabled[i]["command"]); + promises[i] = executeShellCommand({ + command: "aws", + args: enabled[i].command, + }).then(({ stdout }) => stdout); } const result = await Promise.all(promises); @@ -434,11 +438,17 @@ const generators: Record = { return []; } const param = tokens[idx + 1]; - const out = await executeShellCommand( - `aws iam get-instance-profile --instance-profile-name ${param}` - ); + const { stdout } = await executeShellCommand({ + command: "aws", + args: [ + "iam", + "get-instance-profile", + "--instance-profile-name", + param, + ], + }); - const policies = JSON.parse(out as string)["InstanceProfile"]; + const policies = JSON.parse(stdout)["InstanceProfile"]; return policies["Roles"].map((elm) => { return { name: elm["RoleName"], @@ -800,7 +810,7 @@ const generators: Record = { return MultiSuggestionsGenerator(tokens, executeShellCommand, [ ...identityStruct, { - command: "aws iam list-policies --scope Local", + command: ["iam", "list-policies", "--scope", "Local"], parentKey: "Policies", childKey: "Arn", }, diff --git a/src/aws/s3.ts b/src/aws/s3.ts index a4ce53808b32..c88bd6d948f4 100644 --- a/src/aws/s3.ts +++ b/src/aws/s3.ts @@ -53,8 +53,8 @@ const ttl = 30000; const appendFolderPath = ( whatHasUserTyped: string, - baseLSCommand: string -): string => { + baseLSCommand: string[] +): string[] => { let folderPath = ""; const lastSlashIndex = whatHasUserTyped.lastIndexOf("/"); @@ -66,7 +66,7 @@ const appendFolderPath = ( } } - return baseLSCommand + folderPath; + return [...baseLSCommand, folderPath]; }; const postProcessFiles = (out: string, prefix: string): Fig.Suggestion[] => { @@ -153,15 +153,15 @@ const _prefixFileb = "fileb://"; const generators: Record = { listFilesGenerator: { script: (tokens) => { - const baseLSCommand = "\\ls -1ApL "; + const baseLsCommand = ["ls", "-1ApL"]; const whatHasUserTyped = tokens[tokens.length - 1]; // Do not show file suggestions when s3:// typed if (whatHasUserTyped.startsWith(_prefixS3)) { - return ""; + return undefined; } - return appendFolderPath(whatHasUserTyped, baseLSCommand); + return appendFolderPath(whatHasUserTyped, baseLsCommand); }, postProcess: (out) => { return postProcessFiles(out, _prefixFile); @@ -186,13 +186,13 @@ const generators: Record = { // See more: https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-parameters-file.html listBlobsGenerator: { script: (tokens) => { - const baseLSCommand = "\\ls -1ApL "; + const baseLSCommand = ["ls", "-1ApL "]; let whatHasUserTyped = tokens[tokens.length - 1]; if (whatHasUserTyped.startsWith(_prefixFileb)) { whatHasUserTyped = whatHasUserTyped.slice(_prefixFileb.length); } else { - return "echo 'fileb://'"; + return ["echo", "fileb://"]; } return appendFolderPath(whatHasUserTyped, baseLSCommand); @@ -215,7 +215,7 @@ const generators: Record = { listRemoteFilesGenerator: { script: (tokens) => { const whatHasUserTyped = tokens[tokens.length - 1]; - const baseLSCommand = "\\aws s3 ls "; + const baseLsCommand = ["aws", "s3", "ls"]; let folderPath = ""; @@ -226,17 +226,17 @@ const generators: Record = { // then we can assume that the filepath generator is in work // so do not return any s3 related filepaths if (!_prefixS3.startsWith(whatHasUserTyped)) { - return ""; + return undefined; } - return "echo 's3://'"; + return ["echo", "s3://"]; } if (lastSlashIndex > -1) { folderPath = whatHasUserTyped.slice(0, lastSlashIndex + 1); } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }, postProcess: (out) => { if (out == "") { diff --git a/src/aws/secretsmanager.ts b/src/aws/secretsmanager.ts index 9360ec0c1106..d088d21677c2 100644 --- a/src/aws/secretsmanager.ts +++ b/src/aws/secretsmanager.ts @@ -252,10 +252,11 @@ const generators: Record = { return []; } const secretId = tokens[idx + 1]; - const out = await executeShellCommand( - `aws secretsmanager describe-secret --secret-id ${secretId}` - ); - const versions = JSON.parse(out as string)["VersionIdsToStages"]; + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["secretsmanager", "describe-secret", "--secret-id", secretId], + }); + const versions = JSON.parse(stdout)["VersionIdsToStages"]; return Object.keys(versions).map((elm) => ({ name: elm })); } catch (e) { console.log(e); @@ -275,10 +276,11 @@ const generators: Record = { return []; } const secretId = tokens[idx + 1]; - const out = await executeShellCommand( - `aws secretsmanager describe-secret --secret-id ${secretId}` - ); - const versions = JSON.parse(out as string)["VersionIdsToStages"]; + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["secretsmanager", "describe-secret", "--secret-id", secretId], + }); + const versions = JSON.parse(stdout)["VersionIdsToStages"]; return Object.keys(versions).map((elm) => ({ name: versions[elm][0] })); } catch (e) { console.log(e); @@ -299,10 +301,11 @@ const generators: Record = { return []; } const secretId = tokens[idx + 1]; - const out = await executeShellCommand( - `aws secretsmanager describe-secret --secret-id ${secretId}` - ); - const versions = JSON.parse(out as string)["Tags"]; + const { stdout } = await executeShellCommand({ + command: "aws", + args: ["secretsmanager", "describe-secret", "--secret-id", secretId], + }); + const versions = JSON.parse(stdout)["Tags"]; return versions.map((elm) => ({ name: elm["Key"] })); } catch (e) { console.log(e); diff --git a/src/bat.ts b/src/bat.ts index 9ee29589c985..b0f7841954bd 100644 --- a/src/bat.ts +++ b/src/bat.ts @@ -86,7 +86,11 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --wrap unknow 2>&1 >/dev/null | grep possible", + script: [ + "bash", + "-c", + "bat --wrap unknow 2>&1 >/dev/null | grep possible", + ], postProcess: function (out) { return out .trim() @@ -123,7 +127,11 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --color unknow 2>&1 >/dev/null | grep possible", + script: [ + "bash", + "-c", + "bat --color unknow 2>&1 >/dev/null | grep possible", + ], postProcess: function (out) { return out .trim() @@ -148,7 +156,11 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --italic-text unknow 2>&1 >/dev/null | grep possible", + script: [ + "bash", + "-c", + "bat --italic-text unknow 2>&1 >/dev/null | grep possible", + ], postProcess: function (out) { return out .trim() @@ -174,7 +186,11 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --decorations unknow 2>&1 >/dev/null | grep possible", + script: [ + "bash", + "-c", + "bat --decorations unknow 2>&1 >/dev/null | grep possible", + ], postProcess: function (out) { return out .trim() @@ -203,7 +219,11 @@ const completionSpec: Fig.Spec = { args: { name: "", generators: { - script: "bat --paging unknow 2>&1 >/dev/null | grep possible", + script: [ + "bash", + "-c", + "bat --paging unknow 2>&1 >/dev/null | grep possible", + ], postProcess: function (out) { return out .trim() diff --git a/src/black.ts b/src/black.ts index 33f9dbc599f2..4f4df0a523e7 100644 --- a/src/black.ts +++ b/src/black.ts @@ -1,6 +1,6 @@ // https://github.com/psf/black const blackVersions: Fig.Generator = { - script: ["gh","release","list","--repo","psf/black"], + script: ["gh", "release", "list", "--repo", "psf/black"], cache: { ttl: 1000 * 60 * 60 * 24 * 2, // 2 days }, diff --git a/src/brew.ts b/src/brew.ts index d5d10d3f978c..99b87c45ca9a 100644 --- a/src/brew.ts +++ b/src/brew.ts @@ -1,5 +1,5 @@ const servicesGenerator = (action: string): Fig.Generator => ({ - script: "brew services list | sed -e 's/ .*//' | tail -n +2", + script: ["bash", "-c", "brew services list | sed -e 's/ .*//' | tail -n +2"], postProcess: function (out) { return out .split("\n") @@ -34,7 +34,7 @@ const formulaeGenerator: Fig.Generator = { }; const outdatedformulaeGenerator: Fig.Generator = { - script: "brew outdated -q", + script: ["brew", "outdated", "-q"], postProcess: function (out) { return out.split("\n").map((formula) => ({ name: formula, @@ -68,7 +68,11 @@ const generateAllCasks: Fig.Generator = { }, }; const generateAliases: Fig.Generator = { - script: 'find ~/.brew-aliases/ -type f ! -name "*.*" -d 1 | sed "s/.*\\///"', + script: [ + "bash", + "-c", + 'find ~/.brew-aliases/ -type f ! -name "*.*" -d 1 | sed "s/.*\\///"', + ], postProcess: function (out) { return out .split("\n") @@ -1274,7 +1278,7 @@ const completionSpec: Fig.Spec = { isVariadic: true, generators: { - script: ["brew","list","-1","--cask"], + script: ["brew", "list", "-1", "--cask"], postProcess: function (out) { return out.split("\n").map((formula) => { return { diff --git a/src/browser-sync.ts b/src/browser-sync.ts index 48f36cae5d58..4fd800e8ad31 100644 --- a/src/browser-sync.ts +++ b/src/browser-sync.ts @@ -225,8 +225,11 @@ const completionSpec: Fig.Spec = { args: { name: "recipe-name", generators: { - script: + script: [ + "bash", + "-c", "browser-sync recipe ls | tail -n +3 | sed -e 's/^[[:space:]]*//'", + ], splitOn: "\n", cache: { strategy: "max-age", diff --git a/src/bunx.ts b/src/bunx.ts index a5a77ae4a1e5..f3ed883d3489 100644 --- a/src/bunx.ts +++ b/src/bunx.ts @@ -6,7 +6,11 @@ const bunx: Fig.Spec = { name: "command", isCommand: true, generators: { - script: `until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/`, + script: [ + "bash", + "-c", + "until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/`", + ], postProcess: function (out) { const cli = [...npxSuggestions].reduce( (acc, { name }) => [...acc, name], diff --git a/src/cargo.ts b/src/cargo.ts index 9d7dcb2ecf6d..6bcbb51c5476 100644 --- a/src/cargo.ts +++ b/src/cargo.ts @@ -47,37 +47,38 @@ const vcsOptions: { }, ]; -const testGenerator: Fig.Generator = { - cache: { - cacheByDirectory: true, - strategy: "stale-while-revalidate", - ttl: 1000 * 60 * 5, - }, - script: (context) => { - const base = context[context.length - 1]; - // allow split by single colon so that it triggers on a::b: - const indexIntoModPath = Math.max(base.split(/::?/).length, 1); - // split by :: so that tokens with a single colon are allowed - const moduleTokens = base.split("::"); - const lastModule = moduleTokens.pop(); - // check if the token has a : on the end - const hasColon = lastModule[lastModule.length - 1] == ":" ? ":" : ""; - return `cargo t -- --list | awk '/: test$/ { print substr($1, 1, length($1) - 1) }' | awk -F "::" '{ print "${hasColon}"$${indexIntoModPath},int( NF / ${indexIntoModPath} ) }'`; - }, - postProcess: (out) => { - return [...new Set(out.split("\n"))].map((line) => { - const [display, last] = line.split(" "); - const lastModule = parseInt(last); - const displayName = display.replaceAll(":", ""); - const name = displayName.length - ? `${display}${lastModule ? "" : "::"}` - : ""; - return { name, displayName }; - }); - }, - trigger: ":", - getQueryTerm: ":", -}; +// TODO(grant): add this back but better with no awk +// const testGenerator: Fig.Generator = { +// cache: { +// cacheByDirectory: true, +// strategy: "stale-while-revalidate", +// ttl: 1000 * 60 * 5, +// }, +// script: (context) => { +// const base = context[context.length - 1]; +// // allow split by single colon so that it triggers on a::b: +// const indexIntoModPath = Math.max(base.split(/::?/).length, 1); +// // split by :: so that tokens with a single colon are allowed +// const moduleTokens = base.split("::"); +// const lastModule = moduleTokens.pop(); +// // check if the token has a : on the end +// const hasColon = lastModule[lastModule.length - 1] == ":" ? ":" : ""; +// return `cargo t -- --list | awk '/: test$/ { print substr($1, 1, length($1) - 1) }' | awk -F "::" '{ print "${hasColon}"$${indexIntoModPath},int( NF / ${indexIntoModPath} ) }'`; +// }, +// postProcess: (out) => { +// return [...new Set(out.split("\n"))].map((line) => { +// const [display, last] = line.split(" "); +// const lastModule = parseInt(last); +// const displayName = display.replaceAll(":", ""); +// const name = displayName.length +// ? `${display}${lastModule ? "" : "::"}` +// : ""; +// return { name, displayName }; +// }); +// }, +// trigger: ":", +// getQueryTerm: ":", +// }; type Metadata = { packages: Package[]; @@ -160,11 +161,11 @@ const targetGenerator: ({ kind }: { kind?: TargetKind }) => Fig.Generator = ({ kind, }) => ({ custom: async (_, executeShellCommand, context) => { - const out = await executeShellCommand({ + const { stdout } = await executeShellCommand({ command: "cargo", args: ["metadata", "--format-version", "1", "--no-deps"], }); - const manifest: Metadata = JSON.parse(out); + const manifest: Metadata = JSON.parse(stdout); const packages = rootPackageOrLocal(manifest); let targets = packages.flatMap((pkg) => pkg.targets); @@ -318,7 +319,7 @@ const searchGenerator: Fig.Generator = { }; const tripleGenerator: Fig.Generator = { - script: ["rustc","--print","target-list"], + script: ["rustc", "--print", "target-list"], postProcess: (data: string) => { return data .split("\n") @@ -4706,7 +4707,6 @@ const completionSpec: (toolchain?: boolean) => Fig.Spec = ( name: "args", isOptional: true, isVariadic: true, - generators: testGenerator, }, ], }, @@ -5016,7 +5016,11 @@ const completionSpec: (toolchain?: boolean) => Fig.Spec = ( args: { name: "SPEC", generators: { - script: `cargo install --list | \\grep -E "^[a-zA-Z\\-]+\\sv" | cut -d ' ' -f 1`, + script: [ + "bash", + "-c", + `cargo install --list | \\grep -E "^[a-zA-Z\\-]+\\sv" | cut -d ' ' -f 1`, + ], splitOn: "\n", }, isVariadic: true, diff --git a/src/cf.ts b/src/cf.ts index ae22e674520e..58fc2ea82eb6 100644 --- a/src/cf.ts +++ b/src/cf.ts @@ -7,7 +7,7 @@ const postProcessCfList = .map((name) => ({ name, description })); const generateAppNames: Fig.Generator = { - script: `cf apps | cut -d " " -f1`, + script: ["bash", "-c", `cf apps | cut -d " " -f1`], postProcess: postProcessCfList("App name", 4), }; @@ -22,7 +22,7 @@ const generateSpaces: Fig.Generator = { }; const generateServices: Fig.Generator = { - script: `cf services | cut -d " " -f1 `, + script: ["bash", "-c", `cf services | cut -d " " -f1 `], postProcess: postProcessCfList("Service", 4), }; diff --git a/src/chown.ts b/src/chown.ts index 02ec642d0bd9..c271be0bd401 100644 --- a/src/chown.ts +++ b/src/chown.ts @@ -8,13 +8,20 @@ export const existingUsersandGroups: Fig.Generator = { // in the current command. If it is, get the system groups // else retrieve the list of system users if (colonAdded) { - shell = await executeShellCommand( - "dscl . -list /Groups PrimaryGroupID | tr -s ' '| sort -r" - ); + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + "dscl . -list /Groups PrimaryGroupID | tr -s ' '| sort -r", + ], + }); + shell = stdout; } else { - shell = await executeShellCommand( - "dscl . -list /Users UniqueID | tr -s ' '| sort -r" - ); + const { stdout } = await executeShellCommand({ + command: "bash", + args: ["-c", "dscl . -list /Users UniqueID | tr -s ' '| sort -r"], + }); + shell = stdout; } return ( diff --git a/src/coda.ts b/src/coda.ts index e4c07cb61810..61b36cd4c9b3 100644 --- a/src/coda.ts +++ b/src/coda.ts @@ -1,7 +1,11 @@ import { filepaths } from "@fig/autocomplete-generators"; const formulaNames: Fig.Generator = { - script: `grep -A5 --include=\*.ts --exclude-dir=node_modules -r 'addFormula\\|addSyncTable\\|makeFormula\\|makeSyncTable' . | grep -A3 -i formula | grep name: | grep -oE "['\\"]\\w*['\\"]"`, + script: [ + "bash", + "-c", + `grep -A5 --include=\*.ts --exclude-dir=node_modules -r 'addFormula\\|addSyncTable\\|makeFormula\\|makeSyncTable' . | grep -A3 -i formula | grep name: | grep -oE "['\\"]\\w*['\\"]"`, + ], postProcess: (output) => { if (output.trim().length === 0) { return []; diff --git a/src/conda.ts b/src/conda.ts index 622fc1efa2ca..01e66016aca0 100644 --- a/src/conda.ts +++ b/src/conda.ts @@ -56,7 +56,7 @@ const getCondaEnvironments: Fig.Generator = { }; const getCondaConfigs: Fig.Generator = { - script: ["conda","config","--show"], + script: ["conda", "config", "--show"], postProcess: function (out) { const lines = out.split("\n"); const configs: Fig.Suggestion[] = []; diff --git a/src/copilot.ts b/src/copilot.ts index 36c980ae7bde..a292d9fce01c 100644 --- a/src/copilot.ts +++ b/src/copilot.ts @@ -1,7 +1,7 @@ import YAML from "yaml"; const applicationName: Fig.Generator = { - script: ["cat","copilot/.workspace"], + script: ["cat", "copilot/.workspace"], // TODO: I feel like there's a better way to do this. // There's only ever expected to be one `application` key. postProcess: (output) => { diff --git a/src/cordova.ts b/src/cordova.ts index 2110d3e7efc8..e7ae9a749b01 100644 --- a/src/cordova.ts +++ b/src/cordova.ts @@ -24,7 +24,7 @@ const commonOptions: Fig.Option[] = [ ]; const platformGenerator: Fig.Generator = { - script: ["cat","package.json"], + script: ["cat", "package.json"], postProcess: function (out: string) { const suggestions = []; try { diff --git a/src/deno/generators.ts b/src/deno/generators.ts index 76de2774d02d..709214b3c927 100644 --- a/src/deno/generators.ts +++ b/src/deno/generators.ts @@ -165,7 +165,7 @@ export const generateDocs: Fig.Generator = { !token.startsWith("(") ); command.push("--json"); - return command.join(" "); + return command; }, postProcess: (out, tokens) => { const docNodes = JSON.parse(out) as DocNode[]; @@ -191,7 +191,7 @@ type VersionsJSON = { }; export const generateVersions: Fig.Generator = { - script: `curl -sL 'https://cdn.deno.land/deno/meta/versions.json'`, + script: ["curl", "-sL", "https://cdn.deno.land/deno/meta/versions.json"], cache: { ttl: 1000 * 60 * 60 * 24 }, // 24 hours, in milliseconds postProcess: (out) => { const data = JSON.parse(out) as VersionsJSON; @@ -305,17 +305,26 @@ function getConfigPath(tokens: string[]): string | null { async function getDenoConfig( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ): Promise { const configPath = getConfigPath(tokens); let jsonString: string; if (configPath) { - jsonString = await executeShellCommand(`\\cat '${configPath}'`); + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [configPath], + }); + jsonString = stdout; } else { // Move backwards through the directory heirarchy until we find a config file (or hit the root) - jsonString = await executeShellCommand( - "until [[ ( -f deno.json || -f deno.jsonc || $PWD = '/' ) ]]; do cd ..; done; \\cat deno.json 2>/dev/null || \\cat deno.jsonc 2>/dev/null" - ); + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + "until [[ ( -f deno.json || -f deno.jsonc || $PWD = '/' ) ]]; do cd ..; done; \\cat deno.json 2>/dev/null || \\cat deno.jsonc 2>/dev/null", + ], + }); } try { return JSON.parse(stripJsonComments(jsonString)); @@ -351,7 +360,7 @@ export const generateTasks: Fig.Generator = { // --- Generate installed deno scripts export const generateInstalledDenoScripts: Fig.Generator = { - script: "\\find ~/.deno/bin -maxdepth 1 -perm -111 -type f", + script: ["bash", "-c", "\\find ~/.deno/bin -maxdepth 1 -perm -111 -type f"], postProcess: (out) => out .split("\n") diff --git a/src/deployctl.ts b/src/deployctl.ts index 101066ecbe41..62fc4593001f 100644 --- a/src/deployctl.ts +++ b/src/deployctl.ts @@ -67,7 +67,11 @@ const completionSpec: Fig.Spec = { name: "version", isOptional: true, generators: { - script: `curl -sL 'https://cdn.deno.land/deploy/meta/versions.json'`, + script: [ + "curl", + "-sL", + "https://cdn.deno.land/deploy/meta/versions.json", + ], cache: { ttl: 1000 * 60 * 60 * 24 }, // 24 hours, in milliseconds postProcess: (out) => { const data = JSON.parse(out) as VersionsJSON; diff --git a/src/docker-compose.ts b/src/docker-compose.ts index 532e88c31235..af49fadb68ae 100644 --- a/src/docker-compose.ts +++ b/src/docker-compose.ts @@ -1,7 +1,7 @@ const getComposeCommand = (tokens: string[]) => - tokens[0] === "docker" ? "docker compose" : "docker-compose"; + tokens[0] === "docker" ? ["docker", "compose"] : ["docker-compose"]; -const extractFileArgs = (tokens: string[]): string => { +const extractFileArgs = (tokens: string[]): string[] => { const files: string[] = []; for (let i = 0; i < tokens.length - 1; i++) { if (tokens[i] === "-f") { @@ -9,14 +9,14 @@ const extractFileArgs = (tokens: string[]): string => { i += 1; } } - return files.map((f) => `-f ${f}`).join(" "); + return files.flatMap((f) => ["-f", f]); }; const servicesGenerator: Fig.Generator = { script: (tokens) => { const compose = getComposeCommand(tokens); const fileArgs = extractFileArgs(tokens); - return `${compose} ${fileArgs} config --services`; + return [...compose, ...fileArgs, "config", "--services"]; }, splitOn: "\n", }; @@ -25,7 +25,7 @@ const profilesGenerator: Fig.Generator = { script: (tokens) => { const compose = getComposeCommand(tokens); const fileArgs = extractFileArgs(tokens); - return `${compose} ${fileArgs} config --profiles`; + return [...compose, ...fileArgs, "config", "--profiles"]; }, splitOn: "\n", }; diff --git a/src/doppler.ts b/src/doppler.ts index fa5dd6a90c11..7d730c20fcbd 100644 --- a/src/doppler.ts +++ b/src/doppler.ts @@ -64,7 +64,7 @@ const projectsGenerator: Fig.Generator = { cacheKey: "projects", cacheByDirectory: true, }, - script: ["doppler","projects","--json"], + script: ["doppler", "projects", "--json"], postProcess: (out) => { try { const obj = JSON.parse(out); diff --git a/src/dotnet/dotnet-run.ts b/src/dotnet/dotnet-run.ts index c8cc4e23d06b..b7a221f15ce0 100644 --- a/src/dotnet/dotnet-run.ts +++ b/src/dotnet/dotnet-run.ts @@ -58,7 +58,7 @@ const completionSpec: Fig.Spec = { name: "name", suggestions: ["Development", "Staging", "Production"], generators: { - script: ["cat","Properties/launchSettings.json"], + script: ["cat", "Properties/launchSettings.json"], postProcess(out) { const profiles: LaunchProfiles = JSON.parse(out).profiles; diff --git a/src/dotnet/dotnet-tool.ts b/src/dotnet/dotnet-tool.ts index 11306b390eb2..d7e53367ac0c 100644 --- a/src/dotnet/dotnet-tool.ts +++ b/src/dotnet/dotnet-tool.ts @@ -64,10 +64,10 @@ const toolListGenerator: Fig.Generator = { const globalFlags = ["-g", "--global"]; if (context.some((ctx) => globalFlags.includes(ctx))) { - return "dotnet tool list --global"; + return ["dotnet", "tool", "list", "--global"]; } - return "dotnet tool list"; + return ["dotnet", "tool", "list"]; }, postProcess(out) { const lines = out.split("\n").slice(2); diff --git a/src/drush.ts b/src/drush.ts index 7574f6f1e87f..d7f7549ae5a2 100644 --- a/src/drush.ts +++ b/src/drush.ts @@ -38,7 +38,11 @@ const completionSpec: Fig.Spec = { description: "Drush is a command line shell and Unix scripting interface for Drupal", generateSpec: async (tokens, executeShellCommand) => { - const jsonList = await executeShellCommand("drush --format=json"); + const { stdout: jsonList } = await executeShellCommand({ + command: "drush", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--format=json"], + }); const subcommands: Fig.Subcommand[] = []; try { diff --git a/src/dscl.ts b/src/dscl.ts index eb4f0c5364ef..781a0db3cadf 100644 --- a/src/dscl.ts +++ b/src/dscl.ts @@ -85,8 +85,10 @@ const generateDsclPath: Fig.Generator = { } const lastSlashIndex = lastToken.lastIndexOf("/"); const path = lastToken.slice(0, lastSlashIndex < 0 ? 0 : lastSlashIndex); - const command = `dscl ${datasource} -list ${path || "/"}`; - const lines = await executeShellCommand(command); + const { stdout: lines } = await executeShellCommand({ + command: "dscl", + args: [datasource, "-list", path ?? "/"], + }); return lines .trim() .split("\n") diff --git a/src/esbuild.ts b/src/esbuild.ts index 1fdba4d5c28a..53934f64c830 100644 --- a/src/esbuild.ts +++ b/src/esbuild.ts @@ -6,10 +6,14 @@ const icon = const ignoreExtensions = new Set(["", "sample", "env"]); const extensions: Fig.Generator["custom"] = async (_, executeShellCommand) => { - const out = await executeShellCommand( - "find . -depth 3 -type f -name '*.*' -not -path '*/node_modules/*' | sed 's/.*\\.//' | sort -u" - ); - const lines = out.trim().split("\n"); + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + "find . -depth 3 -type f -name '*.*' -not -path '*/node_modules/*' | sed 's/.*\\.//' | sort -u", + ], + }); + const lines = stdout.trim().split("\n"); return lines .filter((line) => !ignoreExtensions.has(line)) .map((line) => ({ name: "." + line })); diff --git a/src/eslint.ts b/src/eslint.ts index 2653d6a9fd09..663f213154ae 100644 --- a/src/eslint.ts +++ b/src/eslint.ts @@ -115,8 +115,11 @@ const completionSpec: Fig.Spec = { args: { name: "Plugin", generators: { - script: + script: [ + "bash", + "-c", "{ ls node_modules ; ls $(npm root -g) ; ls $(yarn global dir)/node_modules/ ; } | cat", + ], postProcess: (out) => out .split("\n") diff --git a/src/example/trigger.ts b/src/example/trigger.ts index 0108abf86ca0..52c6b0210306 100644 --- a/src/example/trigger.ts +++ b/src/example/trigger.ts @@ -7,13 +7,13 @@ // NOTE: replace _prefix_string_for_file_and_folder_suggestions with whatever prefix you'd like e.g. "s3://" const _prefix_string_for_file_and_folder_suggestions = "file://"; -var customArgument = { +var customArgument: Fig.Arg = { name: "FILE/FOLDER", description: "must start with " + _prefix_string_for_file_and_folder_suggestions, generators: { script: (tokens) => { - var baseLSCommand = "\\ls -1ApL "; + var baseLsCommand = ["ls", "-1ApL"]; var whatHasUserTyped = tokens[tokens.length - 1]; if ( @@ -23,7 +23,7 @@ var customArgument = { ) { whatHasUserTyped = whatHasUserTyped.slice(7); } else { - return "echo 'file://'"; + return ["echo", "file://"]; } // Get the folder path to run ls from based on what user has typed @@ -45,7 +45,7 @@ var customArgument = { else folderPath = whatHasUserTyped.slice(0, lastSlashIndex + 1); } - return baseLSCommand + folderPath; + return [...baseLsCommand, folderPath]; }, postProcess: (out) => { if (out.trim() === _prefix_string_for_file_and_folder_suggestions) { diff --git a/src/expo-cli.ts b/src/expo-cli.ts index a5a2ee7572fd..32a0620ce611 100644 --- a/src/expo-cli.ts +++ b/src/expo-cli.ts @@ -34,12 +34,12 @@ const _gen: Record = { }, }, "xcode-configuration": { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["bash", "-c", "xcodebuild -project ios/*.xcodeproj -list -json"], postProcess: (script: string) => JSON.parse(script).project.configurations.map((name) => ({ name })), }, "xcode-scheme": { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["bash", "-c", "xcodebuild -project ios/*.xcodeproj -list -json"], postProcess: (script: string) => JSON.parse(script).project.schemes.map((name) => ({ name })), }, diff --git a/src/ffmpeg.ts b/src/ffmpeg.ts index 00d5f996d94d..3992d115e9ce 100644 --- a/src/ffmpeg.ts +++ b/src/ffmpeg.ts @@ -413,7 +413,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: ["ffmpeg","-codecs"], + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") @@ -924,7 +924,7 @@ const completionSpec: Fig.Spec = { args: { name: "codec", generators: { - script: ["ffmpeg","-codecs"], + script: ["ffmpeg", "-codecs"], postProcess: (out) => { return out .split("\n") diff --git a/src/fig/shared.ts b/src/fig/shared.ts index e1462b725c30..6beca5c8baa8 100644 --- a/src/fig/shared.ts +++ b/src/fig/shared.ts @@ -39,21 +39,6 @@ const graphql = async ({ return JSON.parse(stdout).data; }; -const devCompletionsFolderGenerator: Fig.Generator = { - script: '\\ls -d -1 "$PWD/"**/', - postProcess: (out) => - out.split("\n").map((folder) => { - const paths = folder.split("/"); - paths.pop(); - - return { - name: paths.pop(), - insertValue: folder, - icon: `fig://path/${folder}`, - }; - }), -}; - const disableForCommandsGenerator: Fig.Generator = { script: ["fig", "settings", "autocomplete.disableForCommands"], postProcess: (out) => { @@ -120,25 +105,29 @@ export const themesGenerator: Fig.Generator = { }; export const SETTINGS_GENERATOR: Record = { - "autocomplete.devCompletionsFolder": devCompletionsFolderGenerator, "autocomplete.disableForCommands": disableForCommandsGenerator, "autocomplete.theme": themesGenerator, }; export const subsystemsGenerator: Fig.Generator = { - script: "\\ls ~/.fig/logs", - trigger: (curr, prev) => { - // trigger on new token - return curr.length === 0 && prev.length > 0; - }, - postProcess: (out, tokens) => { - const insertedLogFiles = new Set(tokens.slice(0, -1)); - return out - .split("\n") - .map((name) => name.replace(".log", "")) - .concat("figterm") - .map((name) => ({ name, icon: "🪵" })) - .filter((suggestion) => !insertedLogFiles.has(suggestion.name)); + // script: "\\ls ~/.fig/logs", + // trigger: (curr, prev) => { + // // trigger on new token + // return curr.length === 0 && prev.length > 0; + // }, + // postProcess: (out, tokens) => { + // const insertedLogFiles = new Set(tokens.slice(0, -1)); + // return out + // .split("\n") + // .map((name) => name.replace(".log", "")) + // .concat("figterm") + // .map((name) => ({ name, icon: "🪵" })) + // .filter((suggestion) => !insertedLogFiles.has(suggestion.name)); + // }, + custom: async () => { + return ["figterm", "fig_cli", "fig_desktop", "daemon"].map((name) => ({ + name, + })); }, }; diff --git a/src/fin.ts b/src/fin.ts index 70c96b8fcac6..c6f42bf8b65f 100644 --- a/src/fin.ts +++ b/src/fin.ts @@ -373,8 +373,11 @@ const completionSpec: Fig.Spec = { name: "key-name", isOptional: true, generators: { - script: + script: [ + "bash", + "-c", "\\command ls $HOME/.ssh | \\command grep --color=never -v 'pub'", + ], splitOn: "\n", }, }, @@ -943,9 +946,10 @@ const completionSpec: Fig.Spec = { // Adding dynamic subcommands generateSpec: async (tokens, executeShellCommand) => { var new_subcommands = []; - const available_commands = await executeShellCommand( - "ls -1 ~/.docksal/commands/" - ); + const { stdout: available_commands } = await executeShellCommand({ + command: "bash", + args: ["-c", "ls -1 ~/.docksal/commands/"], + }); for (const command of available_commands.split("\n")) { if (command) { new_subcommands.push({ diff --git a/src/firebase.ts b/src/firebase.ts index bddab501a17c..2747db8f1bba 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -1,5 +1,5 @@ const projectAliasesGenerator: Fig.Generator = { - script: ["firebase","projects:list"], // this calls to a firebase server and is therefore slow + script: ["firebase", "projects:list"], // this calls to a firebase server and is therefore slow postProcess: (out) => { const getAliasRegex = /^│ (\w.*?)│/gm; const aliasesRaw = Array.from(out.matchAll(getAliasRegex)); diff --git a/src/fisher.ts b/src/fisher.ts index 5dd4efe25d67..dbd60c3c26b7 100644 --- a/src/fisher.ts +++ b/src/fisher.ts @@ -70,7 +70,7 @@ const pluginList = [ ]; const installedPlugins: Fig.Generator = { - script: ["fish","-c","fisher list"], + script: ["fish", "-c", "fisher list"], postProcess: (output: string) => { if (!output) { return []; diff --git a/src/git.ts b/src/git.ts index 50dbdcd0fad3..4db00b885ed3 100644 --- a/src/git.ts +++ b/src/git.ts @@ -444,8 +444,11 @@ export const gitGenerators: Record = { }, getStagedFiles: { - script: + script: [ + "bash", + "-c", "git --no-optional-locks status --short | sed -ne '/^M /p' -e '/A /p'", + ], postProcess: postProcessTrackedFiles, }, @@ -457,9 +460,17 @@ export const gitGenerators: Record = { getChangedTrackedFiles: { script: function (context) { if (context.includes("--staged") || context.includes("--cached")) { - return `git --no-optional-locks status --short | sed -ne '/^M /p' -e '/A /p'`; + return [ + "bash", + "-c", + `git --no-optional-locks status --short | sed -ne '/^M /p' -e '/A /p'`, + ]; } else { - return `git --no-optional-locks status --short | sed -ne '/M /p' -e '/A /p'`; + return [ + "bash", + "-c", + `git --no-optional-locks status --short | sed -ne '/M /p' -e '/A /p'`, + ]; } }, postProcess: postProcessTrackedFiles, diff --git a/src/goto.ts b/src/goto.ts index 10e0e2b19b24..81d30584a527 100644 --- a/src/goto.ts +++ b/src/goto.ts @@ -1,10 +1,14 @@ const listTargets: Fig.Generator = { - custom: async (tokens, executeShellCommand) => { - const targets = await executeShellCommand("command cat ~/.config/goto"); + custom: async (tokens, executeShellCommand, context) => { + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${context.environmentVariables["HOME"]}/.config/goto`], + }); const targetSuggestions = new Map(); - for (const target of targets.split("\n")) { + for (const target of stdout.split("\n")) { const splits = target.split(" "); targetSuggestions.set(target, { name: splits[0], diff --git a/src/hexo.ts b/src/hexo.ts index 47d56cf0dd66..b3e2d4b6a903 100644 --- a/src/hexo.ts +++ b/src/hexo.ts @@ -1,5 +1,5 @@ const draftGenerator: Fig.Generator = { - script: "hexo list post | grep -E ^Draft", + script: ["bash", "-c", "hexo list post | grep -E ^Draft"], postProcess: (out) => { return out.split("\n").map(function (file) { const title = file diff --git a/src/hugo.ts b/src/hugo.ts index 4c9bfc490011..f1488beb9b27 100644 --- a/src/hugo.ts +++ b/src/hugo.ts @@ -874,7 +874,7 @@ const completionSpec: Fig.Spec = { { name: "archetype|default", generators: { - script: ["ls","./archetypes/"], + script: ["ls", "./archetypes/"], postProcess: (output) => output.split("\n").map((fileName) => ({ name: fileName.slice(0, fileName.lastIndexOf(".")), diff --git a/src/iconv.ts b/src/iconv.ts index 69e571eef1bf..abcd0b314cc2 100644 --- a/src/iconv.ts +++ b/src/iconv.ts @@ -1,5 +1,5 @@ const encodingGenerator: Fig.Generator = { - script: "iconv -l | command tr ' ' '\\n' | sort", + script: ["bash", "-c", "iconv -l | command tr ' ' '\\n' | sort"], postProcess: (out) => out.split("\n").map((encoding) => ({ name: encoding, diff --git a/src/id.ts b/src/id.ts index 7540a484071f..48e98373e8ea 100644 --- a/src/id.ts +++ b/src/id.ts @@ -59,7 +59,7 @@ const completionSpec: Fig.Spec = { name: "user", isOptional: true, generators: { - script: "dscl . -list /Users | grep -v '^_'", + script: ["bash", "-c", "dscl . -list /Users | grep -v '^_'"], postProcess: (out) => out .trim() diff --git a/src/ignite-cli.ts b/src/ignite-cli.ts index d986e98a5a00..fe42d866a5dc 100644 --- a/src/ignite-cli.ts +++ b/src/ignite-cli.ts @@ -1,5 +1,5 @@ const generatorsGenerator: Fig.Generator = { - script: ["ls","ignite/templates"], + script: ["ls", "ignite/templates"], postProcess: (out) => { if (out.trim() === "") return []; return out.split("\n").map((gen) => ({ diff --git a/src/jenv.ts b/src/jenv.ts index 6cfca182549d..4c24a1ac5ae7 100644 --- a/src/jenv.ts +++ b/src/jenv.ts @@ -1,5 +1,9 @@ const programGenerator: Fig.Generator = { - script: `for i in $(echo $PATH | tr ":" "\\n"); do [[ -d "$i" ]] && find "$i" -maxdepth 1 -type f -perm -111 && find "$i" -maxdepth 1 -type l -perm -111; done`, + script: [ + "bash", + "-c", + `for i in $(echo $PATH | tr ":" "\\n"); do [[ -d "$i" ]] && find "$i" -maxdepth 1 -type f -perm -111 && find "$i" -maxdepth 1 -type l -perm -111; done`, + ], postProcess: (out) => out .split("\n") @@ -54,7 +58,7 @@ const generateAllPlugins: Fig.Generator = { }, }; const generateJEnvVersions: Fig.Generator = { - script: ["jenv","versions","--bare"], + script: ["jenv", "versions", "--bare"], postProcess: function (out) { return out .split("\n") diff --git a/src/kill.ts b/src/kill.ts index 425a35cfcf21..ded07672e2e7 100644 --- a/src/kill.ts +++ b/src/kill.ts @@ -15,7 +15,7 @@ const completionSpec: Fig.Spec = { name: "pid", isVariadic: true, generators: { - script: "ps axo pid,comm | sed 1d", + script: ["bash", "-c", "ps axo pid,comm | sed 1d"], postProcess: (result: string) => { return result.split("\n").map((line) => { const [pid, path] = line.trim().split(/\s+/); diff --git a/src/killall.ts b/src/killall.ts index 8375cf075969..c22e55dd6a4f 100644 --- a/src/killall.ts +++ b/src/killall.ts @@ -43,7 +43,7 @@ const completionSpec: Fig.Spec = { isVariadic: true, generators: { // All processes, only display the path - script: "ps -A -o comm | sort -u", + script: ["bash", "-c", "ps -A -o comm | sort -u"], postProcess: (out) => out .trim() @@ -113,7 +113,7 @@ const completionSpec: Fig.Spec = { args: { name: "user", generators: { - script: "dscl . -list /Users | grep -v '^_'", + script: ["bash", "-c", "dscl . -list /Users | grep -v '^_'"], postProcess: (out) => out .trim() diff --git a/src/kubectx.ts b/src/kubectx.ts index ea5450abe4fc..c1f26d819e68 100644 --- a/src/kubectx.ts +++ b/src/kubectx.ts @@ -59,7 +59,7 @@ const completionSpec: Fig.Spec = { name: "context", generators: [ { - script: `kubectx | grep -v $(kubectx -c)`, + script: ["bash", "-c", "kubectx | grep -v $(kubectx -c)"], postProcess: (out) => out.split("\n").map((item) => ({ name: item, diff --git a/src/kubens.ts b/src/kubens.ts index 17437c6c1949..f3112c2465db 100644 --- a/src/kubens.ts +++ b/src/kubens.ts @@ -26,7 +26,7 @@ const completionSpec: Fig.Spec = { name: "namespace", generators: [ { - script: "kubens | grep -v $(kubens -c)", + script: ["bash", "-c", "kubens | grep -v $(kubens -c)"], postProcess: (out) => out.split("\n").map((item) => ({ name: item, diff --git a/src/login.ts b/src/login.ts index 8055381a3b57..8eaabc4991f0 100644 --- a/src/login.ts +++ b/src/login.ts @@ -25,7 +25,7 @@ const completionSpec: Fig.Spec = { args: { name: "username", generators: { - script: ["cat","/etc/passwd"], + script: ["cat", "/etc/passwd"], postProcess: (out) => { return out.split("\n").map((line) => { const [username] = line.split(":"); diff --git a/src/m.ts b/src/m.ts index d255cb37bb7d..1c18b68d02f1 100644 --- a/src/m.ts +++ b/src/m.ts @@ -27,7 +27,7 @@ const generateVolumes: Fig.Generator = { }; const generateUsers: Fig.Generator = { - script: "m user list | awk '{ print $1 }'", + script: ["bash", "-c", "m user list | awk '{ print $1 }'"], postProcess: (out) => out .trim() @@ -40,7 +40,7 @@ const generateUsers: Fig.Generator = { }; const generateGroups: Fig.Generator = { - script: "m group list | awk '{ print $1 }'", + script: ["bash", "-c", "m group list | awk '{ print $1 }'"], postProcess: (out) => out .trim() @@ -53,12 +53,12 @@ const generateGroups: Fig.Generator = { }; const generateNetworkLocations: Fig.Generator = { - script: "m network location list | tail -n +2", + script: ["bash", "-c", "m network location list | tail -n +2"], splitOn: "\n", }; const generateServices: Fig.Generator = { - script: "launchctl list | awk '{ print $3 }'", + script: ["bash", "-c", "launchctl list | awk '{ print $3 }'"], splitOn: "\n", }; @@ -70,7 +70,7 @@ function getPidIcon(path: string): string { return "fig://" + path.slice(0, idx + 4); } const generatePids: Fig.Generator = { - script: "ps axo pid,comm | sed 1d", + script: ["bash", "-c", "ps axo pid,comm | sed 1d"], postProcess: (result) => { return result.split("\n").map((line) => { const [pid, path] = line.trim().split(/\s+/); @@ -86,8 +86,11 @@ const generatePids: Fig.Generator = { }; const generateWifiNetworks: Fig.Generator = { - script: + script: [ + "bash", + "-c", "networksetup -listallhardwareports | awk '/Wi-Fi/{getline; print $2}' | xargs networksetup -listpreferredwirelessnetworks | tail -n +2", + ], postProcess: (out) => out .trim() diff --git a/src/magento.ts b/src/magento.ts index 40a4d5cd2256..ae4f35c30898 100644 --- a/src/magento.ts +++ b/src/magento.ts @@ -49,9 +49,11 @@ const completionSpec: Fig.Spec = { name: "magento", description: "Open-source E-commerce", generateSpec: async (tokens, executeShellCommand) => { - const command = "bin/magento list --format=json --raw"; - const out = await executeShellCommand(command); - const magento = JSON.parse(out) as BinConsoleJSON; + const { stdout } = await executeShellCommand({ + command: "bin/magento", + args: ["list", "--format=json", "--raw"], + }); + const magento = JSON.parse(stdout) as BinConsoleJSON; const cacheTypes = await getCacheTypes(executeShellCommand); return { diff --git a/src/make.ts b/src/make.ts index 6200bd72a96b..a53ee1b94ca1 100644 --- a/src/make.ts +++ b/src/make.ts @@ -1,13 +1,17 @@ const listTargets: Fig.Generator = { custom: async (tokens, executeShellCommand) => { // Plain target suggestions. These will be overridden if we can find a description for them. - const targets = await executeShellCommand( - "make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\\/\\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u" - ); + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + "make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\\/\\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | sort -u", + ], + }); const targetSuggestions = new Map(); - for (const target of targets.split("\n")) { + for (const target of stdout.split("\n")) { if (target === "Makefile") continue; targetSuggestions.set(target, { name: target.trim(), @@ -17,9 +21,12 @@ const listTargets: Fig.Generator = { }); } - const out = await executeShellCommand("cat [Mm]akefile"); + const { stdout: makefile } = await executeShellCommand({ + command: "cat", + args: ["Makefile", "makefile"], + }); - const matches = out.matchAll( + const matches = makefile.matchAll( /((?:^#.*\n)*)(?:^\.[A-Z_]+:.*\n)*(^\S*?):.*?(?:\s#+[ \t]*(.+))?$/gm ); const specialTargets = new Set([ diff --git a/src/mask.ts b/src/mask.ts index 0b164225d7d4..660321b75d5e 100644 --- a/src/mask.ts +++ b/src/mask.ts @@ -9,17 +9,25 @@ const completionSpec: Fig.Spec = { var maskfileLocationIdx = tokens.indexOf("--maskfile"); - var out; + var out: string; // mask --maskfile path/tp/thing build if (maskfileLocationIdx < 0 || maskfileLocationIdx + 3 > tokens.length) { - out = await executeShellCommand("cat maskfile.md 2> /dev/null"); + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["maskfile.md"], + }); + out = stdout; } else { - out = await executeShellCommand( - `\\cat ${tokens[maskfileLocationIdx + 1]} 2> /dev/null` - ); + const { stdout } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [tokens[maskfileLocationIdx + 1]], + }); + out = stdout; } - if (!out) return { name: "null" }; + if (out === "") return { name: "null" }; return { name: "mask", diff --git a/src/mkinitcpio.ts b/src/mkinitcpio.ts index f87b1e902a4a..ec9673336fb2 100644 --- a/src/mkinitcpio.ts +++ b/src/mkinitcpio.ts @@ -93,7 +93,7 @@ const completionSpec: Fig.Spec = { args: { name: "preset", generators: { - script: ["ls","/etc/mkinitcpio.d"], + script: ["ls", "/etc/mkinitcpio.d"], postProcess: (out) => out .trim() diff --git a/src/multipass.ts b/src/multipass.ts index 1ef9c36c4cf0..6b7cbffa8f80 100644 --- a/src/multipass.ts +++ b/src/multipass.ts @@ -44,7 +44,7 @@ const sharedOpts: Record = { const multipassGenerators: Record = { allAvailableImages: { - script: ["multipass","find","--format=json"], + script: ["multipass", "find", "--format=json"], postProcess: (out) => { const images = JSON.parse(out).images; return Object.keys(images).map((key) => { @@ -56,7 +56,7 @@ const multipassGenerators: Record = { }, }, allAvailableInstances: { - script: ["multipass","list","--format=json"], + script: ["multipass", "list", "--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state !== "Deleted") { @@ -69,7 +69,7 @@ const multipassGenerators: Record = { }, }, allRunningInstances: { - script: ["multipass","list","--format=json"], + script: ["multipass", "list", "--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Running") { @@ -82,7 +82,7 @@ const multipassGenerators: Record = { }, }, allStoppedInstances: { - script: ["multipass","list","--format=json"], + script: ["multipass", "list", "--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Stopped") { @@ -95,7 +95,7 @@ const multipassGenerators: Record = { }, }, allDeletedInstances: { - script: ["multipass","list","--format=json"], + script: ["multipass", "list", "--format=json"], postProcess: (out) => { return JSON.parse(out).list.map((instance) => { if (instance.state === "Deleted") { diff --git a/src/nextflow.ts b/src/nextflow.ts index 1b993ac28113..4894cee25486 100644 --- a/src/nextflow.ts +++ b/src/nextflow.ts @@ -1,5 +1,5 @@ const sessionid: Fig.Generator = { - script: "cat .nextflow/history | awk '{ print $7 }'", + script: ["bash", "-c", "cat .nextflow/history | awk '{ print $7 }'"], postProcess: (output) => { if (output == "") { return []; @@ -14,7 +14,7 @@ const sessionid: Fig.Generator = { }; const runname: Fig.Generator = { - script: "cat .nextflow/history | awk '{ print $4 }'", + script: ["bash", "-c", "cat .nextflow/history | awk '{ print $4 }'"], postProcess: (output) => { if (output == "") { return []; @@ -26,7 +26,11 @@ const runname: Fig.Generator = { }; const projectname: Fig.Generator = { - script: `/bin/sh -c "{ find * -maxdepth 0 -type f -name '*.nf' 2> /dev/null && find $HOME/.nextflow/assets/* -maxdepth 1 -type d | cut -d/ -f6,7 | grep / | grep -v assets; } 2> /dev/null"`, + script: [ + "bash", + "-c", + `{ find * -maxdepth 0 -type f -name '*.nf' 2> /dev/null && find $HOME/.nextflow/assets/* -maxdepth 1 -type d | cut -d/ -f6,7 | grep / | grep -v assets; } 2> /dev/null`, + ], postProcess: (output) => { if (output == "") { return []; @@ -41,7 +45,7 @@ const projectname: Fig.Generator = { }; const dockerimage: Fig.Generator = { - script: `docker images | cut -w -f 1 | grep -v REPOSITORY`, + script: ["bash", "-c", "docker images | cut -w -f 1 | grep -v REPOSITORY"], postProcess: (output) => { if (output == "") { return []; @@ -56,7 +60,11 @@ const dockerimage: Fig.Generator = { }; const secretname: Fig.Generator = { - script: `grep -o '"name": *"[^"]*"' $HOME/.nextflow/secrets/store.json | grep -o '"[^"]*"$' | tr -d \\"`, + script: [ + "bash", + "-c", + `grep -o '"name": *"[^"]*"' $HOME/.nextflow/secrets/store.json | grep -o '"[^"]*"$' | tr -d \\"`, + ], postProcess: (output) => { if (output == "") { return []; diff --git a/src/node.ts b/src/node.ts index 9ec32fd5785e..48207fa70974 100644 --- a/src/node.ts +++ b/src/node.ts @@ -55,8 +55,15 @@ const completionSpec: Fig.Subcommand = { }, ], generateSpec: async (tokens, executeShellCommand) => { - const isAdonisJsonPresentCommand = "test -f .adonisrc.json && echo '1'"; - if ((await executeShellCommand(isAdonisJsonPresentCommand)) === "1") { + const isAdonisJsonPresentCommand = "test -f .adonisrc.json"; + if ( + ( + await executeShellCommand({ + command: "bash", + args: ["-c", "isAdonisJsonPresentCommand"], + }) + ).status === 0 + ) { return { name: "node", subcommands: [ diff --git a/src/npm.ts b/src/npm.ts index e37eaa3b46e7..0ea2647e9f9e 100644 --- a/src/npm.ts +++ b/src/npm.ts @@ -18,7 +18,7 @@ export const createNpmSearchHandler = (keywords?: string[]) => async ( context: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, shellContext: Fig.ShellContext ): Promise => { const searchTerm = context[context.length - 1]; @@ -34,19 +34,26 @@ export const createNpmSearchHandler = : `https://api.npms.io/v2/search/suggestions?q=${searchTerm}&size=20`; // Query the API with the package name - const queryPackages = `curl -s -H "Accept: application/json" "${queryPackagesUrl}"`; + const queryPackages = [ + "-s", + "-H", + "Accept: application/json", + queryPackagesUrl, + ]; // We need to remove the '@' at the end of the searchTerm before querying versions - const queryVersions = `curl -s -H "Accept: application/vnd.npm.install-v1+json" https://registry.npmjs.org/${searchTerm.slice( - 0, - -1 - )}`; + const queryVersions = [ + "-s", + "-H", + "Accept: application/vnd.npm.install-v1+json", + `https://registry.npmjs.org/${searchTerm.slice(0, -1)}`, + ]; // If the end of our token is '@', then we want to generate version suggestions // Otherwise, we want packages const out = (query: string) => - query[query.length - 1] === "@" - ? executeShellCommand(queryVersions) - : executeShellCommand(queryPackages); - + executeShellCommand({ + command: "curl", + args: query[query.length - 1] === "@" ? queryVersions : queryPackages, + }); // If our token starts with '@', then a 2nd '@' tells us we want // versions. // Otherwise, '@' anywhere else in the string will indicate the same. @@ -55,7 +62,7 @@ export const createNpmSearchHandler = : searchTerm.includes("@"); try { - const data = JSON.parse(await out(searchTerm)); + const data = JSON.parse((await out(searchTerm)).stdout); if (shouldGetVersion) { // create dist tags suggestions const versions = Object.entries(data["dist-tags"] || {}).map( @@ -107,10 +114,21 @@ export const npmSearchGenerator: Fig.Generator = { }; const workspaceGenerator: Fig.Generator = { - script: "cat $(npm prefix)/package.json", - postProcess: function (out: string) { - const suggestions = []; + // script: "cat $(npm prefix)/package.json", + custom: async (tokens, executeShellCommand) => { + const { stdout: npmPrefix } = await executeShellCommand({ + command: "npm", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["prefix"], + }); + + const { stdout: out } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${npmPrefix}/package.json`], + }); + const suggestions = []; try { if (out.trim() == "") { return suggestions; @@ -139,7 +157,16 @@ export const dependenciesGenerator: Fig.Generator = { trigger: (newToken) => newToken === "-g" || newToken === "--global", custom: async function (tokens, executeShellCommand) { if (!tokens.includes("-g") && !tokens.includes("--global")) { - const out = await executeShellCommand("cat $(npm prefix)/package.json"); + const { stdout: npmPrefix } = await executeShellCommand({ + command: "npm", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["prefix"], + }); + const { stdout: out } = await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${npmPrefix}/package.json`], + }); const packageContent = JSON.parse(out); const dependencies = packageContent["dependencies"] ?? {}; const devDependencies = packageContent["devDependencies"]; @@ -161,8 +188,11 @@ export const dependenciesGenerator: Fig.Generator = { : "devDependency", })); } else { - const out = await executeShellCommand("ls -1 `npm root -g`"); - return out.split("\n").map((name) => ({ + const { stdout } = await executeShellCommand({ + command: "bash", + args: ["-c", "ls -1 `npm root -g`"], + }); + return stdout.split("\n").map((name) => ({ name, icon: "📦", description: "Global dependency", @@ -177,8 +207,11 @@ export const npmScriptsGenerator: Fig.Generator = { strategy: "stale-while-revalidate", cacheByDirectory: true, }, - script: + script: [ + "bash", + "-c", "until [[ -f package.json ]] || [[ $PWD = '/' ]]; do cd ..; done; cat package.json", + ], postProcess: function (out, [npmClient]) { if (out.trim() == "") { return []; diff --git a/src/nx.ts b/src/nx.ts index 2dd9b1ff73fa..81c56966577b 100644 --- a/src/nx.ts +++ b/src/nx.ts @@ -121,7 +121,7 @@ const fillProjectCaches = (projectJson: NxProject) => { }; const preProcessProjects = async ( - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ) => { if (!nxProjectPathCache.length) { // get project json paths @@ -129,13 +129,24 @@ const preProcessProjects = async ( const { appsDir, libsDir }: { appsDir: string; libsDir: string } = { appsDir: "apps", libsDir: "libs", - ...JSON.parse(await executeShellCommand("cat nx.json")).workspaceLayout, + ...JSON.parse( + ( + await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["nx.json"], + }) + ).stdout + ).workspaceLayout, }; const searchFolders = appsDir === libsDir ? appsDir : `${appsDir} ${libsDir}`; nxProjectPathCache = ( - await executeShellCommand(`find ${searchFolders} -name "project.json"`) - ) + await executeShellCommand({ + command: "find", + args: [searchFolders, "-name", "project.json"], + }) + ).stdout .split("\n") .filter((path) => !!path); } catch (error) { @@ -149,7 +160,13 @@ const preProcessProjects = async ( if (!projectJson) { try { projectJson = JSON.parse( - await executeShellCommand(`cat "${projectJsonPath}"`) + ( + await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [projectJsonPath], + }) + ).stdout ); nxProjectPathWithJsonCache.set(projectJsonPath, projectJson); @@ -167,7 +184,13 @@ const preProcessProjects = async ( if (!nxProjectPathCache.length) { try { nxWorkspaceJsonCache = JSON.parse( - await executeShellCommand(`cat "workspace.json"`) + ( + await executeShellCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["workspace.json"], + }) + ).stdout ); // fill project caches @@ -191,7 +214,7 @@ const listMapKeysGenerator = (map: Map): Fig.Generator => { getQueryTerm: (token) => token.split(",").pop(), custom: async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, generatorContext: Fig.GeneratorContext ) => { const suggestions: Fig.Suggestion[] = []; @@ -214,7 +237,7 @@ const nxGenerators: NxGenerators = { cache: oneDayCache, custom: async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ) => { const suggestions: Fig.Suggestion[] = []; @@ -245,9 +268,9 @@ const nxGenerators: NxGenerators = { script: (context) => { const argument = context.slice(-1)[0]; if (argument.indexOf(":") > -1) { - return `nx list ${argument.split(":")[0]}`; + return ["nx", "list", argument.split(":")[0]]; } else { - return "nx list"; + return ["nx", "list"]; } }, trigger: (newToken, oldToken) => @@ -295,7 +318,7 @@ const nxGenerators: NxGenerators = { // the custom generator custom: async ( _: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ) => { // suggestions to be returned const suggestions: Fig.Suggestion[] = []; @@ -323,7 +346,7 @@ const nxGenerators: NxGenerators = { // the custom generator custom: async ( tokens: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction, + executeShellCommand: Fig.ExecuteCommandFunction, generatorContext: Fig.GeneratorContext ) => { // suggestions to be returned @@ -370,7 +393,7 @@ const nxGenerators: NxGenerators = { }, targets: listMapKeysGenerator(nxTargetWithProjectsCache), workspaceGenerator: { - script: "ls -d tools/generators/*/", + script: ["bash", "-c", "ls -d tools/generators/*/"], cache: oneDayCache, postProcess: (out) => out @@ -665,7 +688,7 @@ const RUN_DERIVED_BASE_TARGETS_WITH_CONFIGURATION = ["build", "serve"]; */ const runDerivedSubcommands = async ( _: string[], - executeShellCommand: Fig.ExecuteShellCommandFunction + executeShellCommand: Fig.ExecuteCommandFunction ): Promise => { const subcommands: Fig.Subcommand[] = []; diff --git a/src/passwd.ts b/src/passwd.ts index 83092153268a..92a80dcad1ab 100644 --- a/src/passwd.ts +++ b/src/passwd.ts @@ -1,5 +1,5 @@ const generateUsers: Fig.Generator = { - script: "dscl . -list /Users | grep -E -v '^_'", + script: ["bash", "-c", "dscl . -list /Users | grep -E -v '^_'"], postProcess: (out) => out .trim() diff --git a/src/pnpm.ts b/src/pnpm.ts index 8d57d54e39c6..1278c2f99430 100644 --- a/src/pnpm.ts +++ b/src/pnpm.ts @@ -964,10 +964,17 @@ const completionSpec: Fig.Spec = { }, filterStrategy: "fuzzy", generateSpec: async (tokens, executeShellCommand) => { - const { script, postProcess } = dependenciesGenerator; + const { script, postProcess } = dependenciesGenerator as Fig.Generator & { + script: string[]; + }; const packages = postProcess( - await executeShellCommand(script as string), + ( + await executeShellCommand({ + command: script[0], + args: script.slice(1), + }) + ).stdout, tokens ).map(({ name }) => name as string); diff --git a/src/pre-commit.ts b/src/pre-commit.ts index bfaabcfc3ce3..3d384122a139 100644 --- a/src/pre-commit.ts +++ b/src/pre-commit.ts @@ -2,7 +2,7 @@ import YAML from "yaml"; import { gitGenerators } from "./git"; const hooksInConfig: Fig.Generator = { - script: ["cat",".pre-commit-config.yaml"], + script: ["cat", ".pre-commit-config.yaml"], postProcess: (output) => { const suggestions: Fig.Suggestion[] = []; diff --git a/src/python.ts b/src/python.ts index 555dbfb0c012..e8ad1793d0e3 100644 --- a/src/python.ts +++ b/src/python.ts @@ -4,11 +4,14 @@ const completionSpec: Fig.Spec = { name: "python", description: "Run the python interpreter", generateSpec: async (tokens, executeShellCommand) => { - const isDjangoManagePyFilePresentCommand = - "cat manage.py | grep -q django; echo $?"; - + const isDjangoManagePyFilePresentCommand = "cat manage.py | grep -q django"; if ( - (await executeShellCommand(isDjangoManagePyFilePresentCommand)) === "0" + ( + await executeShellCommand({ + command: "bash", + args: ["-c", isDjangoManagePyFilePresentCommand], + }) + ).status === 0 ) { return { name: "python", diff --git a/src/python3.ts b/src/python3.ts index a898a4545dd4..7e763c3906d2 100644 --- a/src/python3.ts +++ b/src/python3.ts @@ -4,11 +4,15 @@ const completionSpec: Fig.Spec = { name: "python3", description: "Run the python interpreter", generateSpec: async (tokens, executeShellCommand) => { - const isDjangoManagePyFilePresentCommand = - "cat manage.py | grep -q django; echo $?"; + const isDjangoManagePyFilePresentCommand = "cat manage.py | grep -q django"; if ( - (await executeShellCommand(isDjangoManagePyFilePresentCommand)) === "0" + ( + await executeShellCommand({ + command: "bash", + args: ["-c", isDjangoManagePyFilePresentCommand], + }) + ).status === 0 ) { return { name: "python3", diff --git a/src/rails.ts b/src/rails.ts index cef2246569e6..74952848a192 100644 --- a/src/rails.ts +++ b/src/rails.ts @@ -581,7 +581,10 @@ const defaultCommands: Fig.Subcommand[] = [ isOptional: true, }, async generateSpec(_, executeShellCommand) { - const helpText = await executeShellCommand("rails test --help"); + const { stdout: helpText } = await executeShellCommand({ + command: "rails", + args: ["test", "--help"], + }); const argRegex = /(?:(-[a-zA-Z]), )?(--[^ ]+?)[ =]([A-Z_]+)?[ \r\n]+([^\n]+)/g; @@ -606,7 +609,11 @@ export const railsCommandsGenerator: Fig.Generator = { // parse help text to find more commands let commands: Fig.Subcommand[] = []; try { - const helpText = await executeShellCommand("rails --tasks"); + const { stdout: helpText } = await executeShellCommand({ + command: "rails", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--tasks"], + }); const defaultCommandNames = defaultCommands.map((c) => c.name); const matches = Array.from(helpText.matchAll(/rails ([^ ]+)/g)); @@ -631,9 +638,16 @@ const completionSpec: Fig.Spec = { description: "Ruby on Rails CLI", icon: "https://avatars.githubusercontent.com/u/4223?s=48&v=4", generateSpec: async (_, executeShellCommand) => { - const isRailsDirectory = !!(await executeShellCommand( - `until [[ -f Gemfile ]] || [[ $PWD = '/' ]]; do cd ..; done; if [ -f Gemfile ]; then cat Gemfile | \\grep "gem ['\\"]rails['\\"]"; fi` - )); + const isRailsDirectory = + ( + await executeShellCommand({ + command: "bash", + args: [ + "-c", + `until [[ -f Gemfile ]] || [[ $PWD = '/' ]]; do cd ..; done; if [ -f Gemfile ]; then cat Gemfile | \\grep "gem ['\\"]rails['\\"]"; fi`, + ], + }) + ).status === 0; if (!isRailsDirectory) { return { diff --git a/src/react-native.ts b/src/react-native.ts index 997e9b9fc3c0..3aaa0ff99598 100644 --- a/src/react-native.ts +++ b/src/react-native.ts @@ -14,7 +14,7 @@ const getJsFilesAndFolders = filepaths({ }); const workerGenerator = { - script: ["sysctl","-n","hw.ncpu"], + script: ["sysctl", "-n", "hw.ncpu"], postProcess: (scriptOutput: string) => { return Array.from({ length: Number(scriptOutput) }, (_x, i) => ({ name: `${i}`, @@ -22,7 +22,7 @@ const workerGenerator = { }, }; const xcodeConfigGenerator = { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["bash", "-c", "xcodebuild -project ios/*.xcodeproj -list -json"], postProcess: (scriptOutput: string) => { const configurations = JSON.parse(scriptOutput).project.configurations; @@ -31,7 +31,7 @@ const xcodeConfigGenerator = { }; const xcodeSchemeGenerator = { - script: "xcodebuild -project ios/*.xcodeproj -list -json", + script: ["bash", "-c", "xcodebuild -project ios/*.xcodeproj -list -json"], postProcess: (scriptOutput: string) => { const configurations = JSON.parse(scriptOutput).project.schemes; @@ -90,7 +90,7 @@ const iosGetDevicesGenerator = { }; const iosGetDevicesUdidGenerator = { - script: "xcrun xctrace list devices", + script: ["xcrun", "xctrace", "list", "devices"], postProcess: (scriptOutput: string) => { const devices = scriptOutput .split("\n") @@ -106,8 +106,8 @@ const iosGetDevicesUdidGenerator = { }, }; -const gradleTasksGenerator = { - script: "cd android/ && ./gradlew tasks", +const gradleTasksGenerator: Fig.Generator = { + script: ["bash", "-c", "cd android/ && ./gradlew tasks"], postProcess: (scriptOutput: string) => { const tasks = scriptOutput .split("\n") diff --git a/src/robot.ts b/src/robot.ts index 426a2cf40cd5..a0c42ec02a88 100644 --- a/src/robot.ts +++ b/src/robot.ts @@ -1,8 +1,11 @@ import { filepaths } from "@fig/autocomplete-generators"; const tagsGenerator: Fig.Generator = { - script: + script: [ + "bash", + "-c", 'for i in $(find -E . -regex ".*.robot" -type f); do cat -s $i ; done', + ], postProcess: (out) => { // find all lines with tags // regex: line that starts with 2+ spaces, than '[Tags] ' and words @@ -34,10 +37,14 @@ const variablesGenerator: Fig.Generator = { const finalToken = tokens[tokens.length - 1]; const isKey = !finalToken.includes(":"); if (!isKey) return []; - const out = await executeShellCommand( - 'for i in $(find -E . -regex ".*.(robot|resource)" -type f); do cat -s $i ; done' - ); - const iter = out.matchAll(/^\$\{(.*?)\}/gm); + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + 'for i in $(find -E . -regex ".*.(robot|resource)" -type f); do cat -s $i ; done', + ], + }); + const iter = stdout.matchAll(/^\$\{(.*?)\}/gm); return [...iter] .map((item) => item[1]) .map((variable) => ({ @@ -48,8 +55,11 @@ const variablesGenerator: Fig.Generator = { }; const testCasesGenerator: Fig.Generator = { - script: + script: [ + "bash", + "-c", 'for i in $(find -E . -regex ".*.robot" -type f); do cat -s $i ; done', + ], postProcess: (out) => { // find all parts of the code with test cases // regex: everything after '***Test Cases***' until '***???***') diff --git a/src/rugby.ts b/src/rugby.ts index cb7bab9d6f1b..93c70824c3d3 100644 --- a/src/rugby.ts +++ b/src/rugby.ts @@ -21,14 +21,17 @@ const completionSpec: Fig.Spec = { "Cache Cocoa 🌱 pods for faster rebuild and indexing Xcode project. https://github.com/swiftyfinch/Rugby", name: "rugby", generateSpec: async (tokens, executeShellCommand) => { - const output = await executeShellCommand("rugby plan list"); - if (output === "") { + const { stdout } = await executeShellCommand({ + command: "rugby", + args: ["plan", "list"], + }); + if (stdout === "") { return null; } // Handle `rugby umbrella` command return { name: "plan", - subcommands: output.split("\n").map((plan) => { + subcommands: stdout.split("\n").map((plan) => { return { name: plan, description: `Run plan \"${plan}\"`, diff --git a/src/rustup.ts b/src/rustup.ts index 78a28aa437fb..5f861d56e8ae 100644 --- a/src/rustup.ts +++ b/src/rustup.ts @@ -887,10 +887,10 @@ const completionSpec: Fig.Spec = { isOptional: true, }, generateSpec: async (_tokens, executeShellCommand) => { - const [toolchainOutput] = await Promise.all([ - executeShellCommand("rustup toolchain list"), - ]); - + const { stdout: toolchainOutput } = await executeShellCommand({ + command: "rustup", + args: ["toolchain", "list"], + }); const toolchains: Fig.Option[] = toolchainOutput .split("\n") .map((toolchain) => { diff --git a/src/scarb.ts b/src/scarb.ts index 498c8efbf137..fdff1751d98d 100644 --- a/src/scarb.ts +++ b/src/scarb.ts @@ -331,7 +331,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: ["scarb","metadata","--format-version","1","--no-deps"], + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; @@ -574,7 +580,13 @@ const completionSpec: Fig.Spec = { description: "Packages to run this command on, can be a concrete package name (`foobar`) or a prefix glob (`foo*`) [default: *]", generators: { - script: ["scarb","metadata","--format-version","1","--no-deps"], + script: [ + "scarb", + "metadata", + "--format-version", + "1", + "--no-deps", + ], postProcess: function (out) { const jsonOut = JSON.parse(out); const members = jsonOut.workspace.members; diff --git a/src/shopify/index.ts b/src/shopify/index.ts index 749b10b9a5b3..b2ac44d5111b 100644 --- a/src/shopify/index.ts +++ b/src/shopify/index.ts @@ -6,6 +6,7 @@ export const getVersionCommand: Fig.GetVersionCommand = async ( const versionRegex = /\d+\.\d+\.\d+/; const { stdout } = await executeShellCommand({ command: "shopify", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays args: ["version"], }); return stdout.match(versionRegex)?.[0] ?? ""; diff --git a/src/stepzen.ts b/src/stepzen.ts index 7d6789beb8a9..4ce381dd6cb1 100644 --- a/src/stepzen.ts +++ b/src/stepzen.ts @@ -19,7 +19,10 @@ const endpointsGenerator: Fig.Generator = { }; const importSchemasGenerator: Fig.Generator = { - script: ["curl","https://api.github.com/repos/steprz/stepzen-schemas/contents"], + script: [ + "curl", + "https://api.github.com/repos/steprz/stepzen-schemas/contents", + ], postProcess: (output) => { try { return JSON.parse(output) diff --git a/src/tldr.ts b/src/tldr.ts index e938aee2bcb6..2d960b59e473 100644 --- a/src/tldr.ts +++ b/src/tldr.ts @@ -9,11 +9,19 @@ const windows = `${tldrRc}/pages/windows/`; const isMarkDownRegex = new RegExp(/^.*\.md$/); const wholeTldrPages: Fig.Generator = { - script: () => { - return `command ls -Al ${android} ${common} ${linux} ${osx} ${sunos} ${windows} 2>/dev/null`; - }, - postProcess: (out) => { - return out + custom: async (tokens, executeShellCommand, context) => { + const { stdout } = await executeShellCommand({ + command: "ls", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [ + "-Al", + ...[android, common, linux, osx, sunos, windows].map((path) => + path.replace(/^~/, context.environmentVariables["HOME"]) + ), + ], + }); + + return stdout .split("\n") .filter((line) => isMarkDownRegex.test(line)) .map((line) => { @@ -27,9 +35,7 @@ const wholeTldrPages: Fig.Generator = { }; const linuxTldrPages: Fig.Generator = { - script: () => { - return `command ls -Al ${linux} 2>/dev/null`; - }, + script: ["bash", "-c", `command ls -Al ${linux} 2>/dev/null`], postProcess: (out) => { return out .split("\n") @@ -45,9 +51,7 @@ const linuxTldrPages: Fig.Generator = { }; const osxTldrPages: Fig.Generator = { - script: () => { - return `command ls -l ${osx} 2>/dev/null`; - }, + script: ["bash", "-c", `command ls -l ${osx} 2>/dev/null`], postProcess: (out) => { return out .split("\n") @@ -63,9 +67,7 @@ const osxTldrPages: Fig.Generator = { }; const sunosTldrPages: Fig.Generator = { - script: () => { - return `command ls -l ${sunos} 2>/dev/null`; - }, + script: ["bash", "-c", `command ls -l ${sunos} 2>/dev/null`], postProcess: (out) => { return out .split("\n") diff --git a/src/tmux.ts b/src/tmux.ts index ed3a9f639b5c..6dad63129303 100644 --- a/src/tmux.ts +++ b/src/tmux.ts @@ -1,7 +1,7 @@ const lsArg = (name: string, command: string): Fig.Arg => ({ name, generators: { - script: `tmux ${command}`, + script: ["tmux", command], postProcess: (out) => { return out.split("\n").map((line) => { const content = line.split(":"); diff --git a/src/trex.ts b/src/trex.ts index 8a8b1a648328..af7e6c752a6a 100644 --- a/src/trex.ts +++ b/src/trex.ts @@ -1,5 +1,5 @@ const dependenciesGenerator: Fig.Generator = { - script: ["cat","import_map.json"], + script: ["cat", "import_map.json"], postProcess: function (out) { if (out) { try { @@ -24,7 +24,7 @@ const dependenciesGenerator: Fig.Generator = { }, }; const scriptsGenerator: Fig.Generator = { - script: ["cat","run.json"], + script: ["cat", "run.json"], postProcess: function (out) { if (out) { try { diff --git a/src/tsh.ts b/src/tsh.ts index 1a801110db3d..7efc7e047527 100644 --- a/src/tsh.ts +++ b/src/tsh.ts @@ -93,7 +93,7 @@ const completionSpec: Fig.Spec = { name: "user@hostname", description: "Address of remote machine to log into", generators: { - script: ["tsh","ls","--format=json"], + script: ["tsh", "ls", "--format=json"], postProcess: (out) => { return JSON.parse(out).map((elm) => { return { diff --git a/src/vite.ts b/src/vite.ts index 48b8f1c84c19..6f9950834bf6 100644 --- a/src/vite.ts +++ b/src/vite.ts @@ -109,8 +109,14 @@ const completionSpec: Fig.Spec = { args: { name: "mode", generators: { - script: "\\ls -l1A.env.*", - splitOn: "\n", + script: ["ls", "-l1A"], + postProcess: (out) => + out + .split("\n") + .filter((line) => line.startsWith(".env.")) + .map((name) => ({ + name, + })), }, }, }, diff --git a/src/which.ts b/src/which.ts index 29b872c0c991..a32154a4f2b7 100644 --- a/src/which.ts +++ b/src/which.ts @@ -1,5 +1,9 @@ const programGenerator: Fig.Generator = { - script: `for i in $(echo $PATH | tr ":" "\n"); do find $i -maxdepth 1 -perm -111 -type f; done`, + script: [ + "bash", + "-c", + `for i in $(echo $PATH | tr ":" "\n"); do find $i -maxdepth 1 -perm -111 -type f; done`, + ], postProcess: (out) => out .split("\n") diff --git a/src/wifi-password.ts b/src/wifi-password.ts index ac1adbd98aaf..d0a27a0981f4 100644 --- a/src/wifi-password.ts +++ b/src/wifi-password.ts @@ -4,8 +4,11 @@ const completionSpec: Fig.Spec = { name: "SSID", description: "The name for a Wi-Fi network", generators: { - script: + script: [ + "bash", + "-c", "networksetup -listallhardwareports | awk '/Wi-Fi/{getline; print $2}' | xargs networksetup -listpreferredwirelessnetworks", + ], postProcess: (out) => out .split("\n") diff --git a/src/yalc.ts b/src/yalc.ts index 796957a15ca4..0a0430beef7f 100644 --- a/src/yalc.ts +++ b/src/yalc.ts @@ -1,6 +1,10 @@ const generatePackages: Fig.Generator = { // TODO: use the same as for npm and yarn package.json reverse lookup - script: "command find ~/.yalc/packages -maxdepth 4 -iname 'package.json'", + script: [ + "bash", + "-c", + "command find ~/.yalc/packages -maxdepth 4 -iname 'package.json'", + ], postProcess: (out) => out .split("\n") diff --git a/src/yarn.ts b/src/yarn.ts index 81eef2be0947..0d667bfda0a4 100644 --- a/src/yarn.ts +++ b/src/yarn.ts @@ -51,12 +51,22 @@ export const nodeClis = new Set([ // generate global package list from global package.json file const getGlobalPackagesGenerator: Fig.Generator = { - script: 'cat "$(yarn global dir)/package.json"', - postProcess: (out, tokens) => { - if (out.trim() == "") return []; + custom: async (tokens, executeCommand, generatorContext) => { + const { stdout: yarnGlobalDir } = await executeCommand({ + command: "yarn", + args: ["global", "dir"], + }); + + const { stdout } = await executeCommand({ + command: "cat", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: [`${yarnGlobalDir.trim()}/package.json`], + }); + + if (stdout.trim() == "") return []; try { - const packageContent = JSON.parse(out); + const packageContent = JSON.parse(stdout); const dependencyScripts = packageContent["dependencies"] || {}; const devDependencyScripts = packageContent["devDependencies"] || {}; const dependencies = [ @@ -341,9 +351,15 @@ const commonOptions: Fig.Option[] = [ export const createCLIsGenerator: Fig.Generator = { script: function (context) { - if (context[context.length - 1] === "") return ""; + if (context[context.length - 1] === "") return undefined; const searchTerm = "create-" + context[context.length - 1]; - return `curl -s -H "Accept: application/json" "https://api.npms.io/v2/search?q=${searchTerm}&size=20"`; + return [ + "curl", + "-s", + "-H", + "Accept: application/json", + `https://api.npms.io/v2/search?q=${searchTerm}&size=20`, + ]; }, cache: { ttl: 100 * 24 * 60 * 60 * 3, // 3 days @@ -368,10 +384,14 @@ const completionSpec: Fig.Spec = { description: "Manage packages and run scripts", generateSpec: async (tokens, executeShellCommand) => { const binaries = ( - await executeShellCommand( - `until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/` - ) - ).split("\n"); + await executeShellCommand({ + command: "bash", + args: [ + "-c", + `until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/`, + ], + }) + ).stdout.split("\n"); const subcommands = binaries .filter((name) => nodeClis.has(name)) @@ -1486,17 +1506,26 @@ const completionSpec: Fig.Spec = { description: "Manage workspace", filterStrategy: "fuzzy", generateSpec: async (_tokens, executeShellCommand) => { - const version = await executeShellCommand("yarn --version"); + const version = ( + await executeShellCommand({ + command: "yarn", + // eslint-disable-next-line @withfig/fig-linter/no-useless-arrays + args: ["--version"], + }) + ).stdout; const isYarnV1 = version.startsWith("1."); const getWorkspacesDefinitionsV1 = async () => { - const out = await executeShellCommand(`yarn workspaces info`); + const { stdout } = await executeShellCommand({ + command: "yarn", + args: ["workspaces", "info"], + }); - const startJson = out.indexOf("{"); - const endJson = out.lastIndexOf("}"); + const startJson = stdout.indexOf("{"); + const endJson = stdout.lastIndexOf("}"); return Object.entries( - JSON.parse(out.slice(startJson, endJson + 1)) as Record< + JSON.parse(stdout.slice(startJson, endJson + 1)) as Record< string, { location: string } > @@ -1508,7 +1537,13 @@ const completionSpec: Fig.Spec = { // For yarn >= 2.0.0 const getWorkspacesDefinitionsVOther = async () => { - const out = await executeShellCommand(`yarn workspaces list --json`); + // yarn workspaces list --json + const out = ( + await executeShellCommand({ + command: "yarn", + args: ["workspaces", "list", "--json"], + }) + ).stdout; return out.split("\n").map((line) => JSON.parse(line.trim())); }; @@ -1530,7 +1565,7 @@ const completionSpec: Fig.Spec = { strategy: "stale-while-revalidate", ttl: 60_000, // 60s }, - script: `\\cat ${location}/package.json`, + script: ["cat", `${location}/package.json`], postProcess: function (out: string) { if (out.trim() == "") { return []; diff --git a/src/ykman.ts b/src/ykman.ts index 4c32e821a0c2..813eedfee972 100644 --- a/src/ykman.ts +++ b/src/ykman.ts @@ -2439,7 +2439,11 @@ const completionSpec: Fig.Spec = { args: { name: "SERIAL", generators: { - script: "ykman list | sed -rn 's/.*Serial: (.*)/\\1/p'", + script: [ + "bash", + "-c", + "ykman list | sed -rn 's/.*Serial: (.*)/\\1/p'", + ], postProcess: function (out) { return out.split("\n").map((serial) => { return { name: serial, description: "Yubikey serial" }; diff --git a/src/youtube-dl.ts b/src/youtube-dl.ts index f8427a349f6c..fc574665f4ab 100644 --- a/src/youtube-dl.ts +++ b/src/youtube-dl.ts @@ -943,20 +943,27 @@ const completionSpec: Fig.Spec = { args: { name: "MSO", generators: { - script: (context) => - `youtube-dl ${context.filter((token) => - token.includes("youtube.") - )} --simulate --ap-list-mso | tail -n +3 | tr -s " "`, + custom: async (tokens, executeCommand, context) => { + const { stdout } = await executeCommand({ + command: "youtube-dl", + args: [ + ...tokens.filter((token) => token.includes("youtube.")), + "--simulate", + "--ap-list-mso", + ], + }); - postProcess: (out) => - out.split("\n").map((line) => { - const [name, ...description] = line.split(" "); - - return { - name, - description: description.join(" "), - }; - }), + return stdout + .split("\n") + .slice(3) + .map((line) => { + const [name, ...description] = line.split(" "); + return { + name, + description: description.join(" "), + }; + }); + }, }, }, }, diff --git a/src/z.ts b/src/z.ts index 4487a35f8bfa..e170aaa30d6a 100644 --- a/src/z.ts +++ b/src/z.ts @@ -30,10 +30,13 @@ async function getZHistory( async function getCurrentDirectoryFolders( currentWorkingDirectory: string, - execute: Fig.ExecuteShellCommandFunction + execute: Fig.ExecuteCommandFunction ): Promise { - const out = await execute("ls -d */"); - return out.split("\n").map((line) => { + const { stdout } = await execute({ + command: "bash", + args: ["-c", "ls -d */"], + }); + return stdout.split("\n").map((line) => { const name = line.replace("/", ""); return { name, @@ -143,7 +146,7 @@ const zoxideCompletionSpec: Fig.Spec = { args, }); - return out.split("\n").map((line) => { + return stdout.split("\n").map((line) => { const trimmedLine = line.trim(); const spaceIndex = trimmedLine.indexOf(" "); const score = Number(trimmedLine.slice(0, spaceIndex)); From e047b049aaab8e1c8d3a4cbccf977a21ee2a229a Mon Sep 17 00:00:00 2001 From: Grant Gurvis Date: Mon, 6 Nov 2023 15:20:17 -0800 Subject: [PATCH 3/3] fix type --- src/deno/generators.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/deno/generators.ts b/src/deno/generators.ts index ac188cff63bd..6df0c2450fad 100644 --- a/src/deno/generators.ts +++ b/src/deno/generators.ts @@ -666,7 +666,11 @@ export const generateTasks: Fig.Generator = { // --- Generate installed deno scripts export const generateInstalledDenoScripts: Fig.Generator = { - script: ["bash", "-c", "command find ~/.deno/bin -maxdepth 1 -perm -111 -type f"], + script: [ + "bash", + "-c", + "command find ~/.deno/bin -maxdepth 1 -perm -111 -type f", + ], postProcess: (out) => out .split("\n") @@ -701,7 +705,7 @@ export const generateUrlScript: Fig.Generator = { // There's no simple solution for pasting on Linux, it depends on // whether you use X11 or Wayland. For Wayland on some (most?) distros, // you would have to manually install wl-paste. - script: `pbpaste`, + script: ["pbpaste"], postProcess: (clipboard) => { clipboard = clipboard.trim(); if (!clipboard) {