Skip to content

Commit

Permalink
feat: initial version 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Jan 21, 2020
1 parent 5bea130 commit 000e640
Show file tree
Hide file tree
Showing 11 changed files with 1,740 additions and 3,390 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ lib
*.log
.DS_Store
.env
build
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"useTabs": false,
"printWidth": 80,
"trailingComma": "es5",
"proseWrap": "always"
"proseWrap": "preserve"
}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# :runner: Ultra Runner

Smart and beautiful script runner that hijacks any `npm run`, `yarn` and `npx` calls for **ultra** fast execution.

## Why

| | `npm run` | `npx` | `yarn` | `ultra` |
| ---------------------- | ------------------ | ------------------ | ------------------ | ------------------ |
| `package.json` scripts | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: |
| `./node_modules/.bin/` | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| system binaries | :x: | :white_check_mark: | :x: | :white_check_mark: |
| execution overhead | 250ms | 60ms | 220ms | 80ms |

# TODO

- [ ] add https://github.com/klaussinani/signale
Modes

- raw|silent: stdio:inherit
-

* [ ] add signale
3 changes: 3 additions & 0 deletions __tests__/runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test("should ", () => {
expect(1).toBe(1)
})
2 changes: 2 additions & 0 deletions bin/ultra.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require("../lib/runner").run()
36 changes: 23 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,71 @@
"repository": "http://github.com/folke/ultra-runner",
"author": "Folke Lemaitre <[email protected]>",
"license": "MIT",
"bin": {
"ultra": "bin/ultra.js"
},
"scripts": {
"ultra:a": "echo a",
"ultra:b": "echo b",
"ultra:c": "echo c",
"preultra": "echo pre",
"ultra": "yarn ultra:a && yarn ultra:b && yarn ultra:c",
"true": "true",
"profile": "0x -o --output-dir \"build/0x/{name}{pid}\"",
"tn": "ts-node --transpile-only",
"generate:emoji": "npx ts-node --transpile-only src/scripts/updater.ts",
"generate:config": "npx ts-interface-builder src/config-options.ts",
"prebuild": "npx concurrently -c grey.dim,blue.dim yarn:clean yarn:generate* yarn:build:docs && yarn lint && npx jest",
"prebuild": "yarn clean && yarn lint && yarn test",
"build:docs": "npx copyfiles *.md ../../",
"build:rollup": "npx rollup -c",
"build": "yarn build:rollup",
"clean": "npx rimraf lib coverage *.log",
"test": "npx jest",
"test:cov": "npx jest --coverage",
"lint": "npx concurrently -c grey.dim,blue.dim yarn:lint:eslint yarn:lint:ts",
"lint": "yarn lint:eslint && yarn lint:ts",
"lint:eslint": "npx eslint bin/*.js src/*.ts",
"lint:ts": "npx tsc -p tsconfig.build.json --noEmit",
"lint:fix": "yarn lint:eslint --fix",
"prepublishOnly": "yarn build",
"release": "source .env && CI=true npx semantic-release"
},
"devDependencies": {
"0x": "4.9.1",
"@rollup/plugin-commonjs": "^11.0.1",
"@rollup/plugin-json": "^4.0.1",
"@rollup/plugin-node-resolve": "^7.0.0",
"@rollup/plugin-typescript": "^2.1.0",
"@semantic-release/changelog": "^3.0.6",
"@semantic-release/git": "^8.0.0",
"@types/jest": "^24.0.25",
"@types/node": "^13.1.6",
"@types/node-fetch": "^2.5.4",
"@types/shell-quote": "^1.6.1",
"@types/supports-color": "^5.3.0",
"@typescript-eslint/eslint-plugin": "^2.13.0",
"@typescript-eslint/parser": "^2.13.0",
"concurrently": "^5.0.2",
"conventional-changelog-conventionalcommits": "^4.2.3",
"copyfiles": "^2.1.1",
"devmoji": "^2.0.1",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-jest": "^23.4.0",
"eslint-plugin-prettier": "^3.1.2",
"jest": "^24.9.0",
"node-fetch": "^2.6.0",
"prettier": "^1.19.1",
"rimraf": "^3.0.0",
"rollup": "^1.29.0",
"rollup-plugin-progress": "^1.1.1",
"rollup-plugin-sizes": "^1.0.1",
"rollup-plugin-terser": "^5.2.0",
"semantic-release": "^16.0.1",
"ts-interface-builder": "^0.2.1",
"ts-jest": "^24.3.0",
"ts-node": "^8.6.1",
"tslib": "^1.10.0",
"typescript": "^3.7.4"
},
"dependencies": {
"execa": "^4.0.0",
"package-json": "^6.5.0",
"shell-quote": "^1.7.2"
"chalk": "^3.0.0",
"cli-cursor": "^3.1.0",
"cli-spinners": "^2.2.0",
"commander": "^4.1.0",
"shellwords-ts": "^2.0.4",
"supports-color": "^7.1.0"
}
}
115 changes: 115 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { existsSync } from "fs"
import { resolve } from "path"
import Shellwords from "shellwords-ts"

export type PackageScripts = { scripts: { [key: string]: string } }

export enum CommandType {
script = "script",
bin = "bin",
system = "system",
op = "op",
unknown = "unknown",
}

export class Command {
name: string
kids: Command[] = []

constructor(
public args: string[],
public type: CommandType = CommandType.system
) {
this.name = args?.[0]
}
}

export class CommandParser {
ops = [";", "||", "&&"]

hooks: [string[], string[]][] = [
[["yarn"], ["script", "bin"]],
[
["yarn", "run"],
["script", "bin"],
],
[["npm", "run"], ["script"]],
[["npx"], ["bin"]],
]

constructor(public pkg: PackageScripts) {}

parseArgs(cmd: string) {
const args: string[] = []
Shellwords.split(cmd, rawPart => {
args.push(rawPart.trim())
})
return args
}

createScript(name: string, args: string[] = []) {
const script = this.getScript(name)
if (!script) throw new Error(`Script '${script}' not found in package.json`)
const ret = this.createGroup(script + " " + args.join(" "), false)
ret.name = name
ret.type = CommandType.script
if (this.getScript(`pre${name}`))
ret.kids.unshift(this.createScript(`pre${name}`))
if (this.getScript(`${name}post`))
ret.kids.push(this.createScript(`${name}post`))
return ret
}

createCommand(cmd: string[], allowScriptCmd = true) {
hook: for (const [prefix, types] of this.hooks) {
for (let i = 0; i < prefix.length; i++) {
if (prefix[i] != cmd[i]) continue hook
}
const c = cmd[prefix.length]
if (this.isScript(c) && types.includes(CommandType.script)) {
return this.createScript(c, cmd.slice(prefix.length + 1))
}

if (this.isBin(c) && types.includes(CommandType.bin)) {
return new Command(cmd.slice(prefix.length), CommandType.bin)
}
}
if (allowScriptCmd && this.isScript(cmd[0]))
return this.createScript(cmd[0], cmd.slice(1))
if (this.isBin(cmd[0])) return new Command(cmd, CommandType.bin)
return new Command(cmd, CommandType.system)
}

createGroup(cmd: string, allowScriptCmd = true) {
const args = this.parseArgs(cmd)
const group = new Command([], CommandType.unknown)
const cmdArgs: string[] = []
for (const a of args) {
if (this.ops.includes(a)) {
if (cmdArgs.length)
group.kids.push(this.createCommand(cmdArgs, allowScriptCmd))
cmdArgs.length = 0
group.kids.push(new Command([a], CommandType.op))
} else cmdArgs.push(a)
}
if (cmdArgs.length)
group.kids.push(this.createCommand(cmdArgs, allowScriptCmd))
return group
}

parse(cmd: string) {
return this.createGroup(cmd)
}

isScript(name: string) {
return this.pkg.scripts && name in this.pkg.scripts
}

isBin(name: string) {
return existsSync(resolve("./node_modules/.bin/", name))
}

getScript(name: string) {
return this.pkg.scripts?.[name]
}
}
Loading

0 comments on commit 000e640

Please sign in to comment.