├── .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 |
[](https://github.com/fritx/jayin/actions/workflows/unit-test.yml)
4 | [](https://github.com/fritx/jayin/actions/workflows/unit-test.yml) [](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
--------------------------------------------------------------------------------