Skip to content

pixelastic/firost

Repository files navigation

> firost

Async glob, read and write files.

I was getting tired of having to write the same helpers and wrappers for reading/writing files, so I packaged the best libraries and made an API that works well with async/await.

Filesystem methods

These methods help in finding, reading, moving and writing files on disk.

absolute(filepath)

Converts any filepath to an absolute path, resolving ~/ to home.

absolute('../path/to/file/oops/../.') // /absolute/path/to/file
absolute('~/.npmrc') // /home/tim/.npmrc

copy(source, destination)

Copy file(s)

await copy('index.html', './dist/index.html');
await copy('./foo/*.html', './dist');

download(url, path)

Download a file to specific path on disk

await download('http://www.example.com/file.jpg', './example.jpg');

emptyDir(path)

Empty the content of a directory

await emptyDir('./foo/bar');

exists(path)

Check if a file/directory exists

await exists('./foo/bar/file.ext');

gitRoot()

Returns the path of the closest directory holding a .git folder.

gitRoot(); // /home/tim/projects/firost
gitRoot('~/projects/aberlaas/lib/main.js'); // /home/tim/projects/aberlaas/

glob(pattern, options = {})

Returns an array of filepaths matching the specified glob pattern.

It will return hidden files and directories by default, but you can override it by passing hiddenFiles: false or directories: false to the options argument.

const paths = await glob(['./src/**/*.css', '!./src/**/_*.css']);
const noHiddenFiles = await glob(['./lib/**/*.js'], {
  hiddenFiles: false,
});
const noDirectories = await glob(['./lib'], { directories: false });

isDirectory(path)

Checks if the given path is a directory

if (await isDirectory('./dist')) {
  console.info('Website created');
}

isFile(path)

Checks if the given path is a file

if (await isFile('./package.json')) {
  console.info('File exists');
}

mkdirp(path)

Creates a set of nested directories if they don't yet exist.

await mkdirp('./dist/css');

move(source, destination)

Move file(s)

await move('index.html', 'index.back.html');
await move('./*.html', './dist');

packageRoot()

Returns the path of the closest directory holding a package.json file.

packageRoot(); // /home/tim/projects/firost
packageRoot('~/projects/aberlaas/lib/main.js'); // /home/tim/projects/aberlaas/

read(path)

Returns the textual content of a file located at the specified filepath.

const content = await read('./src/css/style.css');

readJson(path)

Returns the content of a JSON file as a JavaScript object.

const data = await readJson('./records.json');

readJsonUrl(url)

Returns the content of a JSON URL as a JavaScript object.

const data = await readJsonUrl('http://www.example.com/data.json');

remove(target)

Remove file(s)

await remove('index.back.html');
await remove('*.back.html');

urlToFilepath(url)

Converts an URL into a filepath suitable for writing the file to disk.

const filepath = urlToFilepath(
  'http://www.example.com/path/file.html?foo=bar'
);
// http/www.example.com/path/file_foo-bar.html

// Also works with relative utls
const filepath = urlToFilepath('path/file.html?foo=bar');
// path/file_foo-bar.html

watch(pattern, callback, {watcherName})

Watch for file change, and run specified callback with path to changed files.

function doSomething(filepath, type) {
  console.info(`File ${filepath} was just ${type}`);
}
const watcher = await watch(
  './path/to/files/*.jpg',
  doSomething,
  'my-watcher'
);

// To remove the watcher:
await unwatch('my-watch');
// or await unwatch(watcher);
// To remove all watchers:
await unwatchAll();
// To force wait until all watchers are executed
await waitForWatchers();

write(content, destination)

Write content to a file on disk.

await write('This is my content', './dist/content.txt');

writeJson(data, destination)

Write data to a JSON file on disk. Keys will be ordered alphabetically, for easier diffing of the file.

const records = [
  { name: 'foo', value: 2 },
  { value: 3, name: 'bar' },
];
await writeJson(records, './records/records.json');

Shell methods

These methods help abstracting some common CLI tasks

exit

Stop the current process with specified exitCode. Similar to process.exit.

exit(1);

consoleInfo

Display an information message

consoleInfo('Info');
// • Info

consoleWarn

Display a warning message

consoleWarn('Warning');
// ⚠ Warning

consoleError

Display an error message

consoleInfo('Error');
// ✘ Error

consoleSuccess

Display an success message

consoleSuccess('Success');
// ✔ Success

prompt(question)

Interactively ask user a question

const mood = await prompt('How do you feel?');

run(command)

Run a shell command just like in the terminal, but also allows access to stdout, stderr and exit code.

Options

name default value description
shell false Set to true to enable shell-specific feature (like &&, >, etc)
stdout true Set to false to silence stdout
stderr true Set to false to silence stderr
stdin false Set to true to allow user to input keyboard keys. This sets stdout and stderr to true
const { stdout } = await run('echo foo'); // foo
const { stderr } = await run('>&2 echo bar', { shell: true }); // foo
try {
  await run('echo foo && exit 42');
} catch (err) {
  // err.code = 42
  // err.stdout = foo
}

spinner(max)

Creates a spinner with optional max number of elements.

const progress = spinner(10);

progress.tick('Doing task 1');
progress.success('All tasks done');
// or progress.failure('All tasks failed');

shell(command)

Deprecated. Use run instead, it if much more flexible

Run the given command in a shell. Returns stdout, throws with stderr and exitCode.

try {
  const result = await shell('git checkout -b master');
  console.info('Created branch master');
} catch (err) {
  console.error(err.message);
  console.error(err.code);
}

sleep(delay)

Wait for a specific number of milliseconds

await sleep(100); // Wait for 100 ms

tmpDirectory()

Returns a random temporary folder, optionally scoped.

tmpDirectory(); // /tmp/{some-random-uuid}
tmpDirectory('firost/scope/'); // /tmp/firost/scope/{some-random-uuid}

which(command)

Returns the path to an executable on the system. Returns false if none is found.

if (!(await which('convert'))) {
  console.info('You need to install ImageMagick');
}

Utils

cache

Shared singleton to used a proxy cache.

cache.write('foo', 42);
cache.read('foo'); // 42
cache.has('foo'); // true
cache.has('nope'); // false

cache.write('key', { foo: ['one', 'two'], bar: { three: 3 } });
cache.read('key.foo'); // ['one', 'two'];
cache.read('key.bar.three'); // 3

cache.clear('key.foo');
cache.has('key.foo'); // false

cache.clearAll();
cache.has('key'); // false

captureOutput

Silence all output of the specified code, and return it instead

const actual = await captureOutput(async () => {
  console.info("This will not get displayed");
  await run('echo Test');
  await run('./no-existing-script.sh');
});
// actual.stdout: ["This will not get displayed", "Test"]
// actual.stderr: ["Command does not exist"]

error

Returns an Error with both a .code and a .message

throw error('E_ERROR', 'This failed');

normalizeUrl

Normalize a URL

normalizeUrl('http://www.there.com/index.html?sort=asc&name=firost');
// http://www.there.com/?name=firost&sort=asc

pulse

Shared event emitter to listen and emit events

pulse.on('custom', data => {
  console.info(data);
});
pulse.emit('custom', 'Hello');
// Hello

firostImpot(id)

Alternative to the default dynamic import(). Pass forceReload: true as an option to force reloading the latest version on disk, bypassing the singleton cache.

const module = await firostImport('./path/to/module.js');
const updatedModule = await firostImport('./path/to/module.js', {
  forceReload: true,
});

uuid()

Returns a unique ID that could be used in URL of filepaths.

console.info(uuid());
// V1StGXR8_Z5jdHi6B-myT

About

> Toolbox for command-line scripts

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •