├── .github └── workflows │ ├── e2e-test.yml │ └── unit-test.yml ├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json ├── test.js └── wtf.jpg /.github/workflows/e2e-test.yml: -------------------------------------------------------------------------------- 1 | name: e2e test 2 | 3 | on: 4 | push: 5 | branches: [master, dev, ci/**] 6 | pull_request: 7 | branches: [master, dev, ci/**] 8 | workflow_dispatch: 9 | # Keep this to allow for triggering manually 10 | 11 | jobs: 12 | e2e-test: 13 | strategy: 14 | matrix: 15 | # at the time `jayin` was created (May 15, 2016): 16 | # node versions: 0.10.x, 0.12.x, 4 17 | # https://github.com/nodejs/node/blob/main/CHANGELOG.md 18 | # os versions: windows-2016, ubuntu-14.04 19 | # https://en.wikipedia.org/wiki/Ubuntu_version_history 20 | node-version: 21 | # trying to pick up the lowest versions within the supported options 22 | # https://github.com/actions/node-versions/blob/main/versions-manifest.json 23 | # - 8 # skip 24 | # - 10 # skip 25 | # - 12 # skip 26 | # - 14 # skip 27 | - 16 28 | # - 18 # skip 29 | # - 20 # skip 30 | # - 22 # skip 31 | os: 32 | # trying to pick up the lowest versions within the supported options 33 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners 34 | # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#standard-github-hosted-runners-for-public-repositories 35 | - windows-2019 36 | - ubuntu-20.04 37 | # - macos-11 # removed 38 | - macos-12 39 | 40 | runs-on: ${{ matrix.os }} 41 | 42 | steps: 43 | - uses: actions/checkout@v3 44 | - name: Use Node.js ${{ matrix.node-version }} 45 | uses: actions/setup-node@v3 46 | with: 47 | node-version: ${{ matrix.node-version }} 48 | - name: Install 49 | run: | 50 | npm install --production 51 | npm link 52 | 53 | - name: Test default 54 | run: | 55 | echo "[1,2,3,4,5]" | jayin "x.slice(1,4)" > tmp 56 | node -e "assert.equal(fs.readFileSync('tmp','utf8').trim(), '[2,3,4]')" 57 | - name: Test -t 58 | run: | 59 | echo "[1,2,3,4,5]" | jayin -t "JSON.stringify(JSON.parse(x).slice(1,4))" > tmp 60 | node -e "assert.equal(fs.readFileSync('tmp','utf8').trim(), '[2,3,4]')" 61 | - name: Test -ti 62 | run: | 63 | echo "[1,2,3,4,5]" | jayin -ti "JSON.parse(x).slice(1,5)" > tmp 64 | node -e "assert.equal(fs.readFileSync('tmp','utf8').trim(), '[2,3,4,5]')" 65 | - name: Test -to 66 | run: | 67 | echo "[1,2,3,4,5]" | jayin -to "JSON.stringify(x.slice(1,4))" > tmp 68 | node -e "assert.equal(fs.readFileSync('tmp','utf8').trim(), '[2,3,4]')" 69 | -------------------------------------------------------------------------------- /.github/workflows/unit-test.yml: -------------------------------------------------------------------------------- 1 | name: unit test 2 | 3 | on: 4 | push: 5 | branches: [master, dev, ci/**] 6 | pull_request: 7 | branches: [master, dev, ci/**] 8 | workflow_dispatch: 9 | # Keep this to allow for triggering manually 10 | 11 | jobs: 12 | unit-test: 13 | strategy: 14 | matrix: 15 | node-version: 16 | # currently devDependencies require node>=8.9 17 | - 8 18 | - 10 19 | # - 12 # skip 20 | # - 14 # skip 21 | # - 16 # skip 22 | # - 18 # skip 23 | os: 24 | # trying to pick up the lowest versions within the supported options 25 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners 26 | # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#standard-github-hosted-runners-for-public-repositories 27 | - windows-2019 28 | - ubuntu-20.04 29 | # - macos-11 # removed 30 | - macos-12 31 | 32 | runs-on: ${{ matrix.os }} 33 | 34 | steps: 35 | - uses: actions/checkout@v3 36 | - name: Use Node.js ${{ matrix.node-version }} 37 | uses: actions/setup-node@v3 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | - name: Install 41 | run: npm install 42 | - name: Fix nyc coverage report with node8 43 | run: > 44 | npm install --no-save 45 | istanbul-lib-coverage@3.2.0 46 | istanbul-lib-instrument@5.2.1 47 | istanbul-lib-report@3.0.0 48 | istanbul-reports@3.1.5 49 | - name: Test 50 | run: npm test 51 | 52 | - name: Update Coverage Badge 53 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.node-version == 8 }} 54 | uses: we-cli/coverage-badge-action@main 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tmp 3 | .nyc_output 4 | coverage 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-Present Fritz Lin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jayin 2 | 3 |   [![cov](https://we-cli.github.io/jayin/badges/coverage.svg)](https://github.com/fritx/jayin/actions/workflows/unit-test.yml) 4 |   [![unit](https://github.com/fritx/jayin/actions/workflows/unit-test.yml/badge.svg)](https://github.com/fritx/jayin/actions/workflows/unit-test.yml)  [![e2e](https://github.com/fritx/jayin/actions/workflows/e2e-test.yml/badge.svg)](https://github.com/fritx/jayin/actions/workflows/e2e-test.yml) 5 | 6 | Let's say you have a gitignore-like file: 7 | 8 | ```plain 9 | # https://github.com/fritx/dotfiles 10 | # .gitignore 11 | * 12 | !.gitignore 13 | !README.md 14 | !prs-welcome.svg 15 | !.bashrc 16 | !.bash_profile 17 | !.exports 18 | !.aliases 19 | !.editorconfig 20 | ``` 21 | 22 | You want to cp the listed files to another folder. 23 | 24 | Do it in bash? 25 | 26 | ```shell 27 | files=$(cat .gitignore | sed /^\*$/d | sed s/\!//) 28 | for file in $files; do cp $file ./dotfiles/; done 29 | 30 | # or even 31 | cat file | sed /^\*$/d | sed s/\!// \ 32 | | while read -r file; do cp $file ./dotfiles/; done 33 | 34 | # thanks to @congeec, http://v2ex.com/t/278831#reply3 35 | sed /^\*$/d .gitignore | sed s/\!// | xargs -I{} cp {} ./dotfiles/ 36 | ``` 37 | 38 | 39 | 40 | WTF? 41 | 42 | As a node.js developer, what if using just js flow/style? 43 | 44 | ```shell 45 | cat .gitignore | js -ti 'x.trim().split(`\n`).slice(1).map(x => x.slice(1))' \ 46 | | js -e 'exec(`cp ${x} ./dotfiles/`)' 47 | 48 | # same as 49 | cat .gitignore | js -ti 'x.trim().split(`\n`)' \ 50 | | js 'x.slice(1)' \ 51 | | js 'x.map(x => x.slice(1))' \ 52 | | js -e -c 'cp ${x} ./dotfiles/' 53 | ``` 54 | 55 | ```shell 56 | # lodash is also integrated in 57 | # https://github.com/lodash/lodash 58 | echo '[1,2,3,4]' | js '_.filter(x, x => x % 2)' \ 59 | | js '_.reverse(x)' \ 60 | > file 61 | 62 | # or in chain 63 | echo '[1,2,3,4]' \ 64 | | js '_(x).filter(x => x % 2).reverse().value()' \ 65 | > file 66 | ``` 67 | 68 | Don't forget to take an alias if you want. 69 | 70 | ```shell 71 | npm install -g jayin 72 | alias js="jayin" 73 | ``` 74 | 75 | - `-ti`: input as text, no more JSON.parse 76 | - `-to`: output as text, no more JSON.stringify 77 | - `-t`: input/output both as text 78 | - `-e`: for each, in chain 79 | - `-c`: shortcut of exec(cmd) 80 | - `x`: current input value 81 | - `i`: current index value (with -e) 82 | - `_`: lodash 83 | - `exec(cmd)`: child_process.execSync(cmd) 84 | 85 | jayin is based on [through2](https://github.com/rvagg/through2). 86 | 87 | If you've seen anything that is similar to this, don't hesitate to let me know ;) 88 | 89 | ## Compatibility 90 | 91 | | os | Windows | Ubuntu | MacOS | 92 | |:---:|:---:|:---:|:---:| 93 | | supported | ✅ | ✅ | ✅ | 94 | 95 | | node | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 96 | |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| 97 | | supported | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 98 | 99 | See also: [.github/workflows/](https://github.com/fritx/jayin/blob/dev/.github/workflows/) 100 | 101 | ## License 102 | 103 | [MIT](https://github.com/fritx/jayin/blob/dev/LICENSE) License 104 | 105 | Copyright (c) 2016-Present [Fritz Lin](https://github.com/fritx) 106 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // writen in es5, for better env compatability 4 | 5 | // todo: maybe JacksonTian/bufferhelper? 6 | // todo: minimist for friendly cli handling? 7 | // vm: https://nodejs.org/api/vm.html 8 | 'use strict' 9 | var through2 = require('through2') 10 | var _ = require('lodash') 11 | var cp = require('child_process') 12 | var vm = require('vm') 13 | var stdin = process.stdin 14 | var stdout = process.stdout 15 | 16 | var args = process.argv.slice(2) 17 | var forEach = false 18 | var execCmd = false 19 | var textIn = false 20 | var textOut = false 21 | var exprs = [] 22 | 23 | args.forEach(function (arg) { 24 | if (arg === '-e') { // for each 25 | forEach = true 26 | } else if (arg === '-c') { // exec cmd 27 | execCmd = true 28 | } else if (arg === '-ti') { // text input 29 | textIn = true 30 | } else if (arg === '-to') { // text output 31 | textOut = true 32 | } else if (arg === '-t') { // text i/o 33 | textIn = textOut = true 34 | } else { 35 | exprs.push(arg) 36 | } 37 | }) 38 | 39 | var stream = stdin 40 | 41 | exprs.forEach(function (expr) { 42 | if (execCmd) { 43 | expr = 'exec(`' + expr + '`)' 44 | } 45 | if (forEach) { 46 | expr = 'x.forEach(function (x, i) {' + expr + '}), x' 47 | } 48 | 49 | stream = stream.pipe(through2(function (chunk, enc, callback) { 50 | var injson = chunk.toString() 51 | 52 | var inobj = textIn ? injson : JSON.parse(injson) 53 | var sandbox = { 54 | exec: cp.execSync, 55 | _: _, 56 | x: inobj 57 | } 58 | vm.createContext(sandbox) 59 | var outobj = vm.runInContext(expr, sandbox) 60 | 61 | var outjson = textOut ? outobj : JSON.stringify(outobj) 62 | this.push(outjson) 63 | })) 64 | }) 65 | 66 | stream.pipe(stdout) 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jayin", 3 | "version": "0.0.4", 4 | "description": "Piping with js at terminal", 5 | "repository": "fritx/jayin", 6 | "license": "MIT", 7 | "keywords": [ 8 | "bash", 9 | "cli", 10 | "cmd", 11 | "jayin", 12 | "json", 13 | "pipe", 14 | "shell", 15 | "stream" 16 | ], 17 | "files": [ 18 | "index.js" 19 | ], 20 | "bin": { 21 | "jayin": "index.js" 22 | }, 23 | "scripts": { 24 | "test": "nyc -r text -r json-summary mocha --timeout 5000" 25 | }, 26 | "devDependencies": { 27 | "bufferhelper": "^0.2.1", 28 | "mocha": "^2.4.5", 29 | "nyc": "^15.1.0", 30 | "which": "^2.0.2" 31 | }, 32 | "dependencies": { 33 | "lodash": "^4.12.0", 34 | "through2": "^2.0.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, after */ 2 | 'use strict' 3 | const BufferHelper = require('bufferhelper') 4 | const assert = require('assert') 5 | const cp = require('child_process') 6 | const fs = require('fs') 7 | 8 | console.log('process.platform', process.platform) 9 | let nodeCmd = 'node' 10 | if (process.platform === 'win32') { 11 | nodeCmd = require('which').sync('node') 12 | } 13 | console.log('nodeCmd', nodeCmd) 14 | 15 | // todo: more test cases 16 | 17 | describe('jayin', () => { 18 | it ('x', (done) => { 19 | const helper = new BufferHelper() 20 | const js = cp.spawn(nodeCmd, ['./index.js', 'x.slice(2, 4)']) 21 | js.stdout.once('end', () => { 22 | assert.equal(helper.toString(), '[3,4]') 23 | done() 24 | }) 25 | js.stdout.on('data', (chunk) => { 26 | helper.concat(chunk) 27 | }) 28 | js.stdin.end('[1,2,3,4,5]') 29 | }) 30 | 31 | it('-t', (done) => { 32 | const helper = new BufferHelper() 33 | const js = cp.spawn(nodeCmd, ['./index.js', '-t', 'x.slice(1, -1).replace(/,/g, `\n`)']) 34 | js.stdout.once('end', () => { 35 | assert.equal(helper.toString(), '1\n2\n3\n4\n5') 36 | done() 37 | }) 38 | js.stdout.on('data', (chunk) => { 39 | helper.concat(chunk) 40 | }) 41 | js.stdin.end('[1,2,3,4,5]') 42 | }) 43 | 44 | it('-ti', (done) => { 45 | const helper = new BufferHelper() 46 | const js = cp.spawn(nodeCmd, ['./index.js', '-ti', 'a=JSON.parse(x), a.push(999), a']) 47 | js.stdout.once('end', () => { 48 | assert.equal(helper.toString(), '[1,2,3,4,5,999]') 49 | done() 50 | }) 51 | js.stdout.on('data', (chunk) => { 52 | helper.concat(chunk) 53 | }) 54 | js.stdin.end('[1,2,3,4,5]') 55 | }) 56 | 57 | it('-to', (done) => { 58 | const helper = new BufferHelper() 59 | const js = cp.spawn(nodeCmd, ['./index.js', '-to', 'x.join(`\n`)']) 60 | js.stdout.once('end', () => { 61 | assert.equal(helper.toString(), '1\n2\n3\n4\n5') 62 | done() 63 | }) 64 | js.stdout.on('data', (chunk) => { 65 | helper.concat(chunk) 66 | }) 67 | js.stdin.end('[1,2,3,4,5]') 68 | }) 69 | 70 | it('exprs', (done) => { 71 | const helper = new BufferHelper() 72 | const js = cp.spawn(nodeCmd, ['./index.js', 'x.filter(x => x % 2)', 'x.reverse()']) 73 | js.stdout.once('end', () => { 74 | assert.equal(helper.toString(), '[3,1]') 75 | done() 76 | }) 77 | js.stdout.on('data', (chunk) => { 78 | helper.concat(chunk) 79 | }) 80 | js.stdin.end('[1,2,3,4]') 81 | }) 82 | 83 | it('no expr', (done) => { 84 | const helper = new BufferHelper() 85 | const js = cp.spawn(nodeCmd, ['./index.js']) 86 | js.stdout.once('end', () => { 87 | assert.equal(helper.toString(), '[1,2,3,4]') 88 | done() 89 | }) 90 | js.stdout.on('data', (chunk) => { 91 | helper.concat(chunk) 92 | }) 93 | js.stdin.end('[1,2,3,4]') 94 | }) 95 | 96 | it('_, _.filter, _.reverse', (done) => { 97 | const helper = new BufferHelper() 98 | const js = cp.spawn(nodeCmd, ['./index.js', '_(x).filter(x => x % 2).reverse().value()']) 99 | js.stdout.once('end', () => { 100 | assert.equal(helper.toString(), '[3,1]') 101 | done() 102 | }) 103 | js.stdout.on('data', (chunk) => { 104 | helper.concat(chunk) 105 | }) 106 | js.stdin.end('[1,2,3,4]') 107 | }) 108 | 109 | it ('i: x, -e, -c', (done) => { 110 | // https://www.shell-tips.com/2010/06/14/performing-math-calculation-in-bash/ 111 | // const env = { count: 0 } 112 | // execSync('count=0') 113 | const helper = new BufferHelper() 114 | const js = cp.spawn(nodeCmd, ['./index.js', '-e', '-c', "(echo ${i}: ${x}) >> tmp"]) 115 | js.stdout.once('end', () => { 116 | // assert.equal(execSync('echo $count').toString(), '15') 117 | // assert.equal(env.count, '15') 118 | assert.equal(helper.toString(), '["a","b","c"]') // in chain 119 | assert.equal(cp.execSync('cat tmp').toString().replace(/\r\n/g, '\n'), '0: a\n1: b\n2: c\n') 120 | done() 121 | }) 122 | js.stdout.on('data', (chunk) => { 123 | helper.concat(chunk) 124 | }) 125 | js.stdin.end('["a","b","c"]') 126 | }) 127 | 128 | after(() => { 129 | fs.unlinkSync('tmp') 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /wtf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/we-cli/jayin/59361fdc19c87b68d970fbc0b99aaeefc82fa325/wtf.jpg --------------------------------------------------------------------------------