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.
These methods help in finding, reading, moving and writing files on disk.
Converts any filepath to an absolute path, resolving ~/ to home.
absolute('../path/to/file/oops/../.') // /absolute/path/to/file
absolute('~/.npmrc') // /home/tim/.npmrcCopy file(s)
await copy('index.html', './dist/index.html');
await copy('./foo/*.html', './dist');Download a file to specific path on disk
await download('http://www.example.com/file.jpg', './example.jpg');Empty the content of a directory
await emptyDir('./foo/bar');Check if a file/directory exists
await exists('./foo/bar/file.ext');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/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 });Checks if the given path is a directory
if (await isDirectory('./dist')) {
console.info('Website created');
}Checks if the given path is a file
if (await isFile('./package.json')) {
console.info('File exists');
}Creates a set of nested directories if they don't yet exist.
await mkdirp('./dist/css');Move file(s)
await move('index.html', 'index.back.html');
await move('./*.html', './dist');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/Returns the textual content of a file located at the specified filepath.
const content = await read('./src/css/style.css');Returns the content of a JSON file as a JavaScript object.
const data = await readJson('./records.json');Returns the content of a JSON URL as a JavaScript object.
const data = await readJsonUrl('http://www.example.com/data.json');Remove file(s)
await remove('index.back.html');
await remove('*.back.html');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.htmlWatch 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 to a file on disk.
await write('This is my content', './dist/content.txt');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');These methods help abstracting some common CLI tasks
Stop the current process with specified exitCode. Similar to process.exit.
exit(1);Display an information message
consoleInfo('Info');
// • InfoDisplay a warning message
consoleWarn('Warning');
// ⚠ WarningDisplay an error message
consoleInfo('Error');
// ✘ ErrorDisplay an success message
consoleSuccess('Success');
// ✔ SuccessInteractively ask user a question
const mood = await prompt('How do you feel?');Run a shell command just like in the terminal, but also allows access to
stdout, stderr and exit code.
| 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
}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');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);
}Wait for a specific number of milliseconds
await sleep(100); // Wait for 100 msReturns a random temporary folder, optionally scoped.
tmpDirectory(); // /tmp/{some-random-uuid}
tmpDirectory('firost/scope/'); // /tmp/firost/scope/{some-random-uuid}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');
}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'); // falseSilence 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"]Returns an Error with both a .code and a .message
throw error('E_ERROR', 'This failed');Normalize a URL
normalizeUrl('http://www.there.com/index.html?sort=asc&name=firost');
// http://www.there.com/?name=firost&sort=ascShared event emitter to listen and emit events
pulse.on('custom', data => {
console.info(data);
});
pulse.emit('custom', 'Hello');
// HelloAlternative 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,
});Returns a unique ID that could be used in URL of filepaths.
console.info(uuid());
// V1StGXR8_Z5jdHi6B-myT