Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: convert to TypeScript, target Node.js 16 #7

Merged
merged 14 commits into from
Jul 18, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [14, 16, 18]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
coverage
dist
package-lock.json
yarn.lock
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
17 changes: 17 additions & 0 deletions .xo-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"rules": {
"arrow-parens": "off",
"quotes": "off",
"@typescript-eslint/quotes": ["error", "double"],
"object-curly-spacing": "off",
"@typescript-eslint/object-curly-spacing": ["error", "always"],
"@typescript-eslint/no-confusing-void-expression": ["error", { "ignoreArrowShorthand": true }],
"object-shorthand": ["error", "always", { "avoidExplicitReturnArrows": false }],
"@typescript-eslint/keyword-spacing": ["error", { "overrides": {
"if": { "after": false },
"for": {"after": false },
"while": {"after": false },
"catch": {"after": false }
}}]
}
}
140 changes: 0 additions & 140 deletions cli.js

This file was deleted.

File renamed without changes.
73 changes: 32 additions & 41 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,49 @@
},
"type": "module",
"bin": {
"listr": "cli.js"
"listr": "dist/cli.js"
},
"files": [
"cli.js"
"dist"
],
"engines": {
"node": ">=16"
},
"scripts": {
"test": "xo && c8 ava"
"prepublishOnly": "npm run build",
"build": "tsc && chmodx --package",
"postbuild": "replace-in-files dist/cli.js --string='#!/usr/bin/env tsx' --replacement='#!/usr/bin/env node'",
"test": "xo && tsc --noEmit && c8 ava"
},
"dependencies": {
"execa": "^7.1.1",
"listr2": "^5.0.8",
"meow": "^11.0.0"
},
"devDependencies": {
"ava": "^5.2.0",
"c8": "^7.13.0",
"get-bin-path": "^9.0.0",
"xo": "^0.53.1"
},
"engines": {
"node": ">=14.8.0"
"@johnowennixon/chmodx": "^1.1.0",
"@tommy-mitchell/tsconfig": "^1.1.0",
"@types/node": "^20.4.2",
"ava": "^5.3.1",
"c8": "^8.0.0",
"get-bin-path": "^10.0.0",
"is-executable": "^2.0.1",
"replace-in-files-cli": "^2.2.0",
"tsx": "^3.12.7",
"typescript": "~5.1.6",
"xo": "^0.54.2"
},
"xo": {
"rules": {
"quotes": [
"error",
"double"
],
"object-shorthand": [
"error",
"always",
{
"avoidExplicitReturnArrows": false
}
],
"keyword-spacing": [
"error",
{
"overrides": {
"if": {
"after": false
},
"for": {
"after": false
},
"while": {
"after": false
}
}
}
],
"arrow-parens": "off"
}
"ava": {
"files": [
"test/*.ts"
],
"extensions": {
"ts": "module"
},
"nodeArguments": [
"--loader=tsx",
"--no-warnings=ExperimentalWarning"
],
"timeout": "30s"
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests timeout without this, for some reason.

}
}
70 changes: 70 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env tsx
import process from "node:process";
import meow from "meow";
import { $ } from "execa";
import { getTasks } from "./tasks.js";

const cli = meow(`
Usage
$ listr <command> […]

Commands should be space-separated. Commands with spaces in them must be surrounded by quotes.

Equivalent to 'command1 && command2 && …'.

Options
--all-optional Continue executing tasks if one fails. [default: exit]
--hide-timer Disable showing successful task durations. [default: show]

Examples
Run test commands in order
$ listr xo 'c8 ava'

Run commands that can fail
$ listr xo ava tsd --all-optional
`, {
importMeta: import.meta,
description: false,
flags: {
help: {
type: "boolean",
alias: "h",
},
allOptional: {
type: "boolean",
default: false,
},
hideTimer: {
type: "boolean",
default: false,
},
},
});

const { input: commands, flags: { help: helpShortFlag } } = cli;

if(commands.length === 0 || helpShortFlag) {
cli.showHelp(0);
}

const { allOptional, hideTimer } = cli.flags;

const tasks = getTasks({
commands,
exitOnError: !allOptional,
showTimer: !hideTimer,
});

const $$ = $({
shell: true,
reject: false,
all: true,
// Keep command output formatting
stripFinalNewline: false,
env: {
// https://github.com/sindresorhus/execa/issues/69#issuecomment-278693026
FORCE_COLOR: "true", // eslint-disable-line @typescript-eslint/naming-convention
},
});

await tasks.run({ $$ }).catch(() => process.exit(1));
37 changes: 37 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
type ParsedCommand<GetArgs extends boolean> = (
GetArgs extends true
? [commandName: string, args: string[]]
: string
);

/**
* Returns the name of a given command and optionally any of its arguments.
*
* @param command The given command to parse.
* @param getArgs Whether or not to return the command's arguments.
*/
export const parseCommand = <const GetArgs extends boolean = false>(
command: string,
{ getArgs }: { getArgs?: GetArgs } = {},
): ParsedCommand<GetArgs> => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split#using_split
// Since the separator is " ", `command.split` will always have a string at index 0
const [commandName, ...args] = command.split(" ") as [string, ...string[]];
const parsedCommand = (getArgs ? [commandName, args] : commandName) as ParsedCommand<GetArgs>;

return parsedCommand;
};

/**
* Trims command output if it only contains one non-empty line.
*
* @param output Command output.
* @returns The trimmed output, if needed, or the original output.
*/
export const trimIfNeeded = (output: string) => {
const trimmed = output.trim();

return trimmed.includes("\n") ? output : trimmed;
};

// TODO: add individual tests?
Loading