├── .eslintignore ├── .eslintrc ├── .gitignore ├── CHANGELOG.md ├── README.md ├── index.js ├── package.json └── test ├── fixtures └── test.sh └── index.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "es6": true, 5 | "node": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | - basic use case for altering AST to include source attribute 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bash-codegen 2 | 3 | > turn bash AST back into code 4 | 5 | This module will take an AST and add a property to each node named `source`. This attribute when accessed will return the code value of the node. 6 | 7 | ## Installation 8 | 9 | ``` 10 | npm install bash-codegen 11 | ``` 12 | 13 | ## Usage 14 | 15 | > `bash-codegen` will directly edit the AST, to get the bash representation of the AST node, access the source property. 16 | 17 | ```javascript 18 | const codegen = require('bash-codegen'); 19 | 20 | const parsed = codegen(ast).commands.map((code) => { 21 | return code.source + '\n'; 22 | }).join(''); 23 | ``` 24 | 25 | ## Contributing 26 | 27 | When making code changes, please add details to the `CHANGELOG.md` file under the section `## Unreleased`, if it doesn't exist, please add it. 28 | 29 | > example 30 | 31 | ``` 32 | ## Unreleased 33 | 34 | - changed the way something works // each change is a different bullet 35 | ``` 36 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const traverse = require('bash-ast-traverser'); 2 | 3 | module.exports = (ast) => { 4 | return traverse(ast, { 5 | Word: (node) => { 6 | Object.defineProperty(node, 'source', { 7 | get: () => node.text 8 | }) 9 | return node; 10 | }, 11 | AssignmentWord: (node) => { 12 | Object.defineProperty(node, 'source', { 13 | get: () => node.text 14 | }) 15 | return node; 16 | }, 17 | Command: (node) => { 18 | Object.defineProperty(node, 'source', { 19 | get: () => { 20 | const suffix = node.suffix ? node.suffix.map(s => s.source).join(' ') : ''; 21 | const prefix = node.prefix ? node.prefix.map(s => s.source).join(' ') : ''; 22 | const name = node.name ? node.name.source : ''; 23 | return prefix + ' ' + name + ' ' + suffix; 24 | } 25 | }); 26 | return node; 27 | }, 28 | If: (node) => { 29 | Object.defineProperty(node, 'source', { 30 | get: () => { 31 | const _clause = node.clause ? node.clause.commands.map(c => { 32 | return c.source; 33 | }).join(' ') : ''; 34 | const _then = node.then ? node.then.commands.map(c => { 35 | return c.source; 36 | }).join('\n') : ''; 37 | const _else = node.else ? node.else.commands.map(c => { 38 | return c.source; 39 | }).join(' ') : ''; 40 | const _elseif = node.elseif ? node.elseif.commands.map(c => { 41 | return c.source; 42 | }).join(' ') : ''; 43 | 44 | return `if ${_clause}\n${_then && `then\n${_then}\n`}${_else && `else\n${_else}\n`}${_elseif && `elseif\n${_elseif}\n`}\nfi` 45 | } 46 | }); 47 | return node; 48 | } 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bash-codegen", 3 | "version": "0.0.0", 4 | "description": "turn bash AST back into code", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint .", 8 | "test": "tape test/**.js | tap-diff", 9 | "coverage": "tap test/**.js --coverage" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/vorpaljs/bash-codegen.git" 14 | }, 15 | "author": "Gabriel J. Csapo ", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/vorpaljs/bash-codegen/issues" 19 | }, 20 | "homepage": "https://github.com/vorpaljs/bash-codegen#readme", 21 | "devDependencies": { 22 | "bash-parser": "^0.5.0", 23 | "eslint": "^4.1.1", 24 | "tap": "^10.7.0", 25 | "tape": "^4.7.0" 26 | }, 27 | "dependencies": { 28 | "bash-ast-traverser": "^0.5.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/fixtures/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | HELLO=WORLD; 4 | 5 | echo "hello world"; 6 | # hey I am a comment 7 | ls -la 8 | 9 | if [ 1 ] 10 | then 11 | echo "hello again world" 12 | echo "another hello!" 13 | else 14 | echo "nope" 15 | fi 16 | 17 | if [ 1 ] 18 | then 19 | echo "hello and that is it!" 20 | fi 21 | 22 | echo "$HELLO" 23 | 24 | noop 25 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const parse = require('bash-parser'); 4 | const codegen = require('../'); 5 | const test = require('tape'); 6 | 7 | test('bash-codegen', (t) => { 8 | t.plan(1); 9 | 10 | t.test('should be able to convert AST to be the same as the input', (t) => { 11 | const content = fs.readFileSync(path.resolve(__dirname, 'fixtures', 'test.sh')); 12 | const ast = parse(content.toString('utf8')); 13 | 14 | const parsed = codegen(ast).commands.map((code) => { 15 | return code.source + '\n'; 16 | }).join(''); 17 | 18 | t.deepEqual(parsed, 'HELLO=WORLD \n echo hello world\n ls -la\nif [ 1 ]\nthen\n echo hello again world\n echo another hello!\nelse\n echo nope\n\nfi\nif [ 1 ]\nthen\n echo hello and that is it!\n\nfi\n echo "$HELLO"\n noop \n'); 19 | t.end(); 20 | }); 21 | 22 | }); 23 | --------------------------------------------------------------------------------