ci: add linting & formatting

Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
Chapman Pendery
2023-10-08 01:33:07 -07:00
parent 84acea01da
commit af29b9ce6e
11 changed files with 4320 additions and 40 deletions
+30
View File
@@ -0,0 +1,30 @@
module.exports = {
env: {
es2021: true,
node: true,
},
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended", "prettier"],
overrides: [
{
env: {
node: true,
},
files: [".eslintrc.{js,cjs}"],
parserOptions: {
sourceType: "script",
},
},
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
plugins: ["@typescript-eslint", "react"],
rules: {},
settings: {
react: {
version: "detect",
},
},
};
+5
View File
@@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"printWidth": 160
}
+4225
View File
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -15,7 +15,9 @@
"scripts": {
"build": "tsc",
"start": "node ./build/index.js",
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"lint": "eslint src/ --ext .ts,.tsx && prettier src/ --check",
"lint:fix": "eslint src/ --ext .ts,.tsx --fix && prettier src/ --write"
},
"repository": {
"type": "git",
@@ -41,8 +43,14 @@
"@tsconfig/node18": "^18.2.2",
"@types/jest": "^29.5.5",
"@types/react": "^18.2.24",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@withfig/autocomplete-types": "^1.28.0",
"eslint": "^8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-react": "^7.33.2",
"jest": "^29.7.0",
"prettier": "3.0.3",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
}
+3 -1
View File
@@ -36,6 +36,8 @@ export const runGenerator = async (generator: Fig.Generator, tokens: string[]):
suggestions.push(...(await runTemplates(template)));
}
return suggestions;
} catch (e) {}
} catch (e) {
/* empty */
}
return suggestions;
};
+2 -7
View File
@@ -32,14 +32,9 @@ const lex = (command: string): CommandToken[] => {
return;
}
if (
readingQuotedString &&
char === readingQuoteChar &&
command.at(idx - 1) !== "\\"
) {
if (readingQuotedString && char === readingQuoteChar && command.at(idx - 1) !== "\\") {
readingQuotedString = false;
const complete =
idx + 1 < command.length && spaceRegex.test(command[idx + 1]);
const complete = idx + 1 < command.length && spaceRegex.test(command[idx + 1]);
tokens.push({
token: command.slice(readingIdx, idx + 1),
complete,
+24 -13
View File
@@ -1,5 +1,6 @@
import speclist, {
diffVersionedCompletions as versionedSpeclist,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
} from "@withfig/autocomplete/build/index.js";
import { parseCommand, CommandToken } from "./parser.js";
@@ -7,6 +8,7 @@ import { getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from ".
import { SuggestionBlob } from "./model.js";
import { buildExecuteShellCommand } from "./utils.js";
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- recursive type, setting as any
const specSet: any = {};
(speclist as string[]).forEach((s) => {
let activeSet = specSet;
@@ -45,6 +47,7 @@ const lazyLoadSpec = async (key: string): Promise<Fig.Spec | undefined> => {
return (await import(`@withfig/autocomplete/build/${key}.js`)).default;
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- will be implemented in below TODO
const lazyLoadSpecLocation = async (location: Fig.SpecLocation): Promise<Fig.Spec | undefined> => {
return; //TODO: implement spec location loading
};
@@ -72,8 +75,8 @@ const getPersistentOptions = (persistentOptions: Fig.Option[], options?: Fig.Opt
const persistentOptionNames = new Set(persistentOptions.map((o) => (typeof o.name === "string" ? [o.name] : o.name)).flat());
return persistentOptions.concat(
(options ?? []).filter(
(o) => (typeof o.name == "string" ? !persistentOptionNames.has(o.name) : o.name.some((n) => !persistentOptionNames.has(n))) && o.isPersistent === true
)
(o) => (typeof o.name == "string" ? !persistentOptionNames.has(o.name) : o.name.some((n) => !persistentOptionNames.has(n))) && o.isPersistent === true,
),
);
};
@@ -82,7 +85,7 @@ const getSubcommand = (spec?: Fig.Spec): Fig.Subcommand | undefined => {
if (spec == null) return;
if (typeof spec === "function") {
const potentialSubcommand = spec();
if (potentialSubcommand.hasOwnProperty("name")) {
if (Object.prototype.hasOwnProperty.call(potentialSubcommand, "name")) {
return potentialSubcommand as Fig.Subcommand;
}
return;
@@ -101,7 +104,7 @@ const genSubcommand = async (command: string, parentCommand: Fig.Subcommand): Pr
// this pulls in the spec from the load spec and overwrites the subcommand in the parent with the loaded spec.
// then it returns the subcommand and clears the loadSpec field so that it doesn't get called again
switch (typeof subcommand.loadSpec) {
case "function":
case "function": {
const partSpec = await subcommand.loadSpec(command, executeShellCommand);
if (partSpec instanceof Array) {
const locationSpecs = (await Promise.all(partSpec.map((s) => lazyLoadSpecLocation(s)))).filter((s) => s != null) as Fig.Spec[];
@@ -112,23 +115,31 @@ const genSubcommand = async (command: string, parentCommand: Fig.Subcommand): Pr
loadSpec: undefined,
};
return (parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx];
} else if (partSpec.hasOwnProperty("type")) {
} else if (Object.prototype.hasOwnProperty.call(partSpec, "type")) {
const locationSingleSpec = await lazyLoadSpecLocation(partSpec as Fig.SpecLocation);
(parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx] = { ...subcommand, ...(getSubcommand(locationSingleSpec) ?? []), loadSpec: undefined };
(parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx] = {
...subcommand,
...(getSubcommand(locationSingleSpec) ?? []),
loadSpec: undefined,
};
return (parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx];
} else {
(parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx] = { ...subcommand, ...partSpec, loadSpec: undefined };
return (parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx];
}
case "string":
}
case "string": {
const spec = await lazyLoadSpec(subcommand.loadSpec as string);
(parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx] = { ...subcommand, ...(getSubcommand(spec) ?? []), loadSpec: undefined };
return (parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx];
case "object":
}
case "object": {
(parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx] = { ...subcommand, ...(subcommand.loadSpec ?? {}), loadSpec: undefined };
return (parentCommand.subcommands as Fig.Subcommand[])[subcommandIdx];
case "undefined":
}
case "undefined": {
return subcommand;
}
}
};
@@ -149,7 +160,7 @@ const runOption = async (
option: Fig.Option,
subcommand: Fig.Subcommand,
persistentOptions: Fig.Option[],
acceptedTokens: CommandToken[]
acceptedTokens: CommandToken[],
): Promise<SuggestionBlob | undefined> => {
if (tokens.length === 0) {
throw new Error("invalid state reached, option expected but no tokens found");
@@ -170,7 +181,7 @@ const runArg = async (
persistentOptions: Fig.Option[],
acceptedTokens: CommandToken[],
fromOption: boolean,
fromVariadic: boolean
fromVariadic: boolean,
): Promise<SuggestionBlob | undefined> => {
if (args.length === 0) {
return runSubcommand(tokens, subcommand, persistentOptions, acceptedTokens, true, !fromOption);
@@ -218,7 +229,7 @@ const runSubcommand = async (
persistentOptions: Fig.Option[] = [],
acceptedTokens: CommandToken[] = [],
argsDepleted = false,
argsUsed = false
argsUsed = false,
): Promise<SuggestionBlob | undefined> => {
if (tokens.length === 0) {
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens);
@@ -244,7 +255,7 @@ const runSubcommand = async (
tokens.slice(1),
nextSubcommand,
getPersistentOptions(persistentOptions, subcommand.options),
getPersistentTokens(acceptedTokens.concat(activeToken))
getPersistentTokens(acceptedTokens.concat(activeToken)),
);
}
+12 -12
View File
@@ -57,7 +57,7 @@ function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string
suggestions: T[],
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined,
suggestionType: Fig.SuggestionType | undefined
suggestionType: Fig.SuggestionType | undefined,
): Suggestion[] {
if (!partialCmd) return suggestions.map((s) => toSuggestion(s, undefined, suggestionType)).filter((s) => s != null) as Suggestion[];
@@ -129,7 +129,7 @@ const generatorSuggestions = async (
generator: Fig.SingleOrArray<Fig.Generator> | undefined,
acceptedTokens: CommandToken[],
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined
partialCmd: string | undefined,
): Promise<Suggestion[]> => {
const generators = generator instanceof Array ? generator : generator ? [generator] : [];
const tokens = acceptedTokens.map((t) => t.token);
@@ -140,7 +140,7 @@ const generatorSuggestions = async (
const templateSuggestions = async (
templates: Fig.Template | undefined,
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined
partialCmd: string | undefined,
): Promise<Suggestion[]> => {
return filter<Fig.Suggestion>(await runTemplates(templates ?? []), filterStrategy, partialCmd, undefined);
};
@@ -148,7 +148,7 @@ const templateSuggestions = async (
const suggestionSuggestions = (
suggestions: (string | Fig.Suggestion)[] | undefined,
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined
partialCmd: string | undefined,
): Suggestion[] => {
const cleanedSuggestions = suggestions?.map((s) => (typeof s === "string" ? { name: s } : s)) ?? [];
return filter<Fig.Suggestion>(cleanedSuggestions ?? [], filterStrategy, partialCmd, undefined);
@@ -157,7 +157,7 @@ const suggestionSuggestions = (
const subcommandSuggestions = (
subcommands: Fig.Subcommand[] | undefined,
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined
partialCmd: string | undefined,
): Suggestion[] => {
return filter<Fig.Subcommand>(subcommands ?? [], filterStrategy, partialCmd, "subcommand");
};
@@ -166,7 +166,7 @@ const optionSuggestions = (
options: Fig.Option[] | undefined,
acceptedTokens: CommandToken[],
filterStrategy: FilterStrategy | undefined,
partialCmd: string | undefined
partialCmd: string | undefined,
): Suggestion[] => {
const usedOptions = new Set(acceptedTokens.filter((t) => t.isOption).map((t) => t.token));
const validOptions = options?.filter((o) => o.exclusiveOn?.every((exclusiveOption) => !usedOptions.has(exclusiveOption)) ?? true);
@@ -188,7 +188,7 @@ export const getSubcommandDrivenRecommendation = async (
partialCmd: string | undefined,
argsDepleted: boolean,
argsFromSubcommand: boolean,
acceptedTokens: CommandToken[]
acceptedTokens: CommandToken[],
): Promise<SuggestionBlob | undefined> => {
if (argsDepleted && argsFromSubcommand) {
return;
@@ -212,8 +212,8 @@ export const getSubcommandDrivenRecommendation = async (
suggestions: removeEmptySuggestion(
removeDuplicateSuggestions(
suggestions.sort((a, b) => b.priority - a.priority),
acceptedTokens
)
acceptedTokens,
),
),
};
};
@@ -224,7 +224,7 @@ export const getArgDrivenRecommendation = async (
persistentOptions: Fig.Option[],
partialCmd: string | undefined,
acceptedTokens: CommandToken[],
variadicArgBound: boolean
variadicArgBound: boolean,
): Promise<SuggestionBlob | undefined> => {
const activeArg = args[0];
const allOptions = persistentOptions.concat(subcommand.options ?? []);
@@ -243,8 +243,8 @@ export const getArgDrivenRecommendation = async (
suggestions: removeEmptySuggestion(
removeDuplicateSuggestions(
suggestions.sort((a, b) => b.priority - a.priority),
acceptedTokens
)
acceptedTokens,
),
),
argumentDescription: activeArg.description ?? activeArg.name,
};
+1 -1
View File
@@ -36,7 +36,7 @@ export const runTemplates = async (template: Fig.TemplateStrings[] | Fig.Templat
case "help":
return helpTemplate();
}
})
}),
)
).flat();
};
+2 -1
View File
@@ -6,8 +6,9 @@ type ExecuteShellCommandTTYResult = {
export const buildExecuteShellCommand =
(timeout: number) =>
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: use cwd in the future
async (command: string, cwd?: string): Promise<string> => {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
exec(command, { timeout }, (_, stdout, stderr) => {
resolve(stdout || stderr);
});
+7 -4
View File
@@ -87,22 +87,25 @@ export const bind = async (shell: Shell): Promise<void> => {
await fsAsync.mkdir(saConfigPath);
}
switch (shell) {
case Shell.Bash:
case Shell.Bash: {
const bashConfigPath = path.join(os.homedir(), ".bashrc");
await fsAsync.appendFile(bashConfigPath, `\n${bashScriptCommand()}`);
await fsAsync.copyFile(path.join(__dirname, "..", "..", "shell", "key-bindings.bash"), path.join(os.homedir(), ".sa", "key-bindings.bash"));
break;
case Shell.Powershell:
}
case Shell.Powershell: {
const powershellConfigPath = path.join(os.homedir(), "Documents", "WindowsPowershell", "Microsoft.PowerShell_profile.ps1");
await fsAsync.appendFile(powershellConfigPath, `\n${powershellScriptCommand()}`);
await fsAsync.copyFile(
path.join(__dirname, "..", "..", "shell", "key-bindings-powershell.ps1"),
path.join(os.homedir(), ".sa", "key-bindings-powershell.ps1")
path.join(os.homedir(), ".sa", "key-bindings-powershell.ps1"),
);
break;
case Shell.Pwsh:
}
case Shell.Pwsh: {
await fsAsync.appendFile(pwshConfigPath(), `\n${pwshScriptCommand()}`);
await fsAsync.copyFile(path.join(__dirname, "..", "..", "shell", "key-bindings-pwsh.ps1"), path.join(os.homedir(), ".sa", "key-bindings-pwsh.ps1"));
break;
}
}
};