Skip to content

Commit eb7460d

Browse files
author
Zack Argyle
committed
Add 'node' command
1 parent 0eb7173 commit eb7460d

File tree

8 files changed

+113
-26
lines changed

8 files changed

+113
-26
lines changed

demo/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ const structure = {
3131
file2: { content: 'The is the content for file2 in the <public> directory.' },
3232
file3: { content: 'The is the content for file3 in the <public> directory.' },
3333
},
34+
'alert.js': { content: 'alert("Hello world")' },
3435
'README.md': { content: '✌⊂(✰‿✰)つ✌ Thanks for checking out the tool! There is a lot that you can do with react-bash and I\'m excited to see all of the fun commands and projects build on top of it!' },
3536
};
3637

src/bash.js

+8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ export default class Bash {
3030
}
3131
}
3232

33+
executeShell(input, state) {
34+
return Object.assign({}, state, {
35+
history: state.history.concat({
36+
value: Util.evaluate(input),
37+
}),
38+
});
39+
}
40+
3341
parseInput(input) {
3442
const tokens = input.trim().split(/ +/);
3543
const command = tokens.shift();

src/commands.js

+25-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Util from './util';
2-
import { Errors } from './const';
2+
import { Errors, Shells } from './const';
33

4-
const helpCommands = ['clear', 'ls', 'cat', 'mkdir', 'cd', 'pwd'];
4+
const helpCommands = ['clear', 'ls', 'cat', 'mkdir', 'cd', 'pwd', 'node'];
55

66
export const help = {
77
exec: ({ history, structure, cwd }) => {
@@ -49,22 +49,30 @@ export const cat = {
4949
exec: (state, args) => {
5050
const { history, structure, cwd } = state;
5151
const path = args[0];
52-
const relativePath = path.split('/');
53-
const fileName = relativePath.pop();
54-
const fullPath = Util.extractPath(relativePath.join('/'), cwd);
55-
const { err, dir } = Util.getDirectoryByPath(structure, fullPath);
56-
if (err) {
57-
return Util.reportError(state, err, path);
58-
} else if (!dir[fileName]) {
59-
return Util.reportError(state, Errors.NO_SUCH_FILE, path);
60-
} else if (!dir[fileName].hasOwnProperty('content')) {
61-
return Util.reportError(state, Errors.IS_A_DIRECTORY, path);
62-
} else {
52+
return Util.getFile(path, state, file => {
6353
return { cwd, structure,
6454
history: history.concat({
65-
value: dir[fileName].content,
55+
value: file.content,
6656
}),
6757
};
58+
});
59+
},
60+
};
61+
62+
export const node = {
63+
exec: (state, args) => {
64+
const { history, structure, cwd } = state;
65+
const path = args[0];
66+
if (path) {
67+
return Util.getFile(path, state, file => {
68+
return { cwd, structure,
69+
history: history.concat({
70+
value: Util.evaluate(file.content),
71+
}),
72+
};
73+
});
74+
} else {
75+
return { cwd, structure, history, shell: Shells.NODE };
6876
}
6977
},
7078
};
@@ -108,13 +116,11 @@ export const cd = {
108116
};
109117

110118
export const pwd = {
111-
exec: (state) => {
112-
const { history, structure, cwd } = state;
113-
const directory = `/${cwd}`;
114-
119+
exec: ({ history, structure, cwd }) => {
120+
const value = `/${cwd}`;
115121
return {
116122
cwd, structure,
117-
history: history.concat({ value: directory }),
123+
history: history.concat({ value }),
118124
};
119125
},
120126
};

src/component.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ export default class Terminal extends Component {
9595
} else if (evt.which === C_CHAR_CODE) {
9696
if (this.ctrlPressed) {
9797
this.refs.input.value = '';
98+
if (this.state.shell) {
99+
this.setState({ shell: null });
100+
}
98101
}
99102
} else if (evt.which === UP_CHAR_CODE) {
100103
if (this.Bash.hasPrevCommand()) {
@@ -116,8 +119,11 @@ export default class Terminal extends Component {
116119

117120
// Execute command
118121
const input = evt.target[0].value;
119-
const newState = this.Bash.execute(input, this.state);
120-
this.setState(newState);
122+
if (this.state.shell) {
123+
this.setState(this.Bash.executeShell(input, this.state));
124+
} else {
125+
this.setState(this.Bash.execute(input, this.state));
126+
}
121127
this.refs.input.value = '';
122128
}
123129

@@ -132,7 +138,7 @@ export default class Terminal extends Component {
132138

133139
render() {
134140
const { prefix, theme } = this.props;
135-
const { history, cwd } = this.state;
141+
const { history, cwd, shell } = this.state;
136142
const style = Styles[theme] || Styles.light;
137143
return (
138144
<div className="ReactBash" style={style.ReactBash}>
@@ -144,7 +150,9 @@ export default class Terminal extends Component {
144150
<div style={style.body} onClick={() => this.refs.input.focus()}>
145151
{history.map(this.renderHistoryItem(style))}
146152
<form onSubmit={evt => this.handleSubmit(evt)} style={style.form}>
147-
<span style={style.prefix}>{`${prefix} ~${cwd} $`}</span>
153+
{shell ? <span style={style.shell}>{'>'}</span> : (
154+
<span style={style.prefix}>{`${prefix} ~${cwd} $`}</span>
155+
)}
148156
<input
149157
autoComplete="off"
150158
onKeyDown={this.handleKeyDown}

src/const.js

+4
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ export const Errors = {
77
NOT_A_DIRECTORY: '-bash: cd: $1: Not a directory',
88
IS_A_DIRECTORY: 'cat: $1: Is a directory',
99
};
10+
11+
export const Shells = {
12+
NODE: 'NODE',
13+
};

src/styles.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ BaseStyles.input = {
5454
padding: '0',
5555
};
5656

57-
BaseStyles.prefix = {
57+
BaseStyles.prefix = BaseStyles.shell = {
5858
marginRight: '5px',
5959
};
6060

src/util.js

+25
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,28 @@ export function getDirectoryByPath(structure, relativePath) {
6868
}
6969
return { dir };
7070
}
71+
72+
export function getFile(path, state, callback) {
73+
const { structure, cwd } = state;
74+
const relativePath = path.split('/');
75+
const fileName = relativePath.pop();
76+
const fullPath = extractPath(relativePath.join('/'), cwd);
77+
const { err, dir } = getDirectoryByPath(structure, fullPath);
78+
if (err) {
79+
return reportError(state, err, path);
80+
} else if (!dir[fileName]) {
81+
return reportError(state, Errors.NO_SUCH_FILE, path);
82+
} else if (!dir[fileName].hasOwnProperty('content')) {
83+
return reportError(state, Errors.IS_A_DIRECTORY, path);
84+
} else {
85+
return callback(dir[fileName]);
86+
}
87+
}
88+
89+
export function evaluate(code) {
90+
try {
91+
return JSON.stringify(eval(code));
92+
} catch (error) {
93+
return `${error.name}: ${error.message}`;
94+
}
95+
}

tests/commands.js

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import chai from 'chai';
2+
import sinon from 'sinon';
23
import { stateFactory } from './factories';
34
import Bash from '../src/bash';
45
import * as BaseCommands from '../src/commands';
5-
import { Errors } from '../src/const';
6+
import { Errors, Shells } from '../src/const';
67

78
describe('bash commands', () => {
89
let bash;
@@ -147,6 +148,40 @@ describe('bash commands', () => {
147148

148149
});
149150

151+
describe('node', () => {
152+
153+
it('should exist', () => {
154+
chai.assert.isFunction(bash.commands.node.exec);
155+
});
156+
157+
it('should set `shell` state variable to Shells.NODE', () => {
158+
const state = stateFactory();
159+
const { shell } = bash.commands.node.exec(state, {});
160+
chai.assert.strictEqual(shell, Shells.NODE);
161+
});
162+
163+
it('should eval a file\'s contents', () => {
164+
const state = stateFactory();
165+
const expected = state.structure.file1.content;
166+
const stub = sinon.stub(global, 'eval').returns(expected);
167+
const { history } = bash.commands.node.exec(state, { 0: 'file1' });
168+
chai.assert.strictEqual(history.length, 1);
169+
chai.assert.strictEqual(history[0].value, `"${expected}"`);
170+
stub.restore();
171+
});
172+
173+
it('should eval a file\'s contents from path', () => {
174+
const state = stateFactory();
175+
const expected = state.structure.dir1.dir1File.content;
176+
const stub = sinon.stub(global, 'eval').returns(expected);
177+
const { history } = bash.commands.node.exec(state, { 0: 'dir1/dir1File' });
178+
chai.assert.strictEqual(history.length, 1);
179+
chai.assert.strictEqual(history[0].value, `"${expected}"`);
180+
stub.restore();
181+
});
182+
183+
});
184+
150185
describe('mkdir', () => {
151186

152187
it('should exist', () => {
@@ -229,7 +264,7 @@ describe('bash commands', () => {
229264

230265
});
231266

232-
describe('cd', () => {
267+
describe('pwd', () => {
233268
it('should exist', () => {
234269
chai.assert.isFunction(bash.commands.pwd.exec);
235270
});

0 commit comments

Comments
 (0)