Useful tools for automating stuff with Node, like making small changes to config files or updating a dependency.
Install via npm:
npm i @sebalon/scripting
Functions for running shell commands.
Promisified version of child_process.exec
.
Executes command
within a shell and return an object containing stdout and stderr, or throws an error if command
completes with a non-zero exit code.
See the official Node documentation here for more details.
const output = await exec('echo hello');
// => { stdout: 'hello\n', stderr: '' }
Executes command
within a shell and returns its output (stdout), or throws an error if command
completes with a non-zero exit code.
By default, leading and trailing whitespace is trimmed from the output. You can disable this by passing trim: false
in the options.
const output = await sh('echo hello');
// => 'hello'
const untrimmed = await sh('echo hello', { trim: false });
// => 'hello\n'
Functions for manipulating files.
The main use-case for these is to make small modifications to files that already exist. Other file operations (delete, copy, rename) are already easy enough using Node's fs/promises
API. Documentation for these can be found here.
Check whether the given file or directory exists.
if (await exists('somefile')) {
// do something
}
Work with a text file.
Opens the file, passes the content to callback
, then saves it back if any changes were made.
Line endings in the content are normalised to \n
, and returned to the OS-specific line ending on save (as defined by os.EOL
).
await withFile('example.txt', (f) => {
f.replaceAll('old', 'new');
f.appendLine('footer');
f.insertLine('fizz', { aboveEvery: 'buzz' });
f.deleteLine('bye');
});
For all available methods see the Text
class.
Work with a JSON file.
Opens the file, parses its content and passes to callback
, then reserialises back to the file.
An attempt will be made to preserve indentation, based on the first indented line found. This should be adequate for well-formatted documents.
await withJsonFile('example.json', (f) => {
f.foo.bar.baz = 42;
});
Work with a YAML file.
Opens the file, parses its content and passes to callback
, then reserialises back to the file.
The callback
is passed a Document
instance which allows precise editing of YAML content, including comments and spaces. See the yaml
package docs for documentation.
await withYamlFile('example.yaml', (f) => {
f.setIn(['foo', 'bar', 'baz'], 42);
});
Work on text files that match the given search criteria.
// replace every occurrance of 'foo' with 'bar' in every JS file
await withFiles({
include: '**.js',
containing: 'foo',
}, (f) => {
f.replaceAll('foo', 'bar');
});
sh
has a mock mode that allows you to control its output, which can be useful when testing scripts. Activate it using sh.mock()
. This returns an object with methods to control sh
behaviour. You can provide sequential mock results to return, either as default (ignoring the command being run) or matching to a specific command.
When you're done, use sh.restore()
to stop mocking.
// activate mock mode
const mock = sh.mock();
// add some default mocks (you can chain these methods)
mock
.returns({ stdout: 'first call' })
.returns({ stdout: 'second call' });
// add some command-specific mocks
mock
.command('cat file.txt').returns({ stdout: 'file contents' })
.command(/echo/).returns({ stdout: 'mock echo' });
await sh('some arbitrary command');
// => 'first call'
await sh('some arbitrary command');
// => 'second call'
await sh('echo "hi there"');
// => 'mock echo'
await sh('cat file.txt');
// => 'file contents'
// assert that all expected `sh` calls were made
mock.assertDone();
sh.restore();