mrm-core
Utilities to write codemods for config files (JSON, YAML, INI, Markdown, etc.). Can be used to make tasks for Mrm.
Example
Add ESLint to your project:
const { json, lines, packageJson, install } = require('mrm-core');
module.exports = function(config) {
const preset = config('preset', 'tamia');
const packages = ['eslint', `eslint-config-${preset}`];
const eslintrc = json('.eslintrc');
if (!eslintrc.get('extends').startsWith(preset)) {
eslintrc.set('extends', preset).save();
}
lines('.eslintignore')
.add('node_modules')
.save();
const pkg = packageJson()
.setScript('lint', 'eslint . --fix')
.setScript('pretest', 'npm run line')
.save();
install(packages);
};
module.exports.description = 'Adds ESLint with a custom preset';
Read more in the docs, and this task is already included by default.
You can find more examples in my dotfiles repository.
You don’t have to use mrm-core with mrm, you can run this tasks from your own code:
const get = require('lodash/get');
const addEslint = require('./tasks/eslint');
const config = {
preset: 'airbnb'
};
const getConfig = (prop, defaultValue) =>
get(config, prop, defaultValue);
addEslint(getConfig);
Installation
npm install --save-dev mrm-core
API
Work with files
- Do not overwrite original files, unless you want to.
- All functions (except getters) can be chained.
save()
will create file if it doesn’t exist or update it with new data.save()
will write file to disk only if the new content is different from the original file.save()
will try to keep formatting (indentation, end of file new line) of the original file or use style from EditorConfig.
JSON
API:
const { json } = require('mrm-core');
const file = json('file name', { default: 'values' });
file.exists();
file.get();
file.get('key.subkey', 'default value');
file.set('key.subkey', 'value');
file.set({ key: value });
file.unset('key.subkey');
file.merge({ key: value });
file.save();
file.delete();
Example:
json('.eslintrc')
.merge({
extends: 'eslint-config-recommended'
})
.save();
YAML
API:
const { yaml } = require('mrm-core');
const file = yaml('file name', { default: 'values' });
file.exists();
file.get();
file.get('key.subkey', 'default value');
file.set('key.subkey', 'value');
file.set({ key: value });
file.unset('key.subkey');
file.merge({ key: value });
file.save();
file.delete();
Example:
yaml('.travis.yml')
.set('language', 'node_js')
.set('node_js', [4, 6])
.save();
INI
API:
const { ini } = require('mrm-core');
const file = ini('file name', 'comment');
file.exists();
file.get();
file.get('section name');
file.set('section name', { key: value });
file.unset('section name');
file.save();
file.save({ withSpaces: false });
file.delete();
Example:
const { ini } = require('mrm-core');
ini('.editorconfig', 'editorconfig.org')
.set('_global', { root: true })
.set('*', {
indent_style: 'tab',
end_of_line: 'lf'
})
.save();
Result:
root = true
[*]
indent_style = tab
end_of_line = lf
New line separated text files
API:
const { lines } = require('mrm-core');
const file = lines('file name', ['default', 'values']);
file.exists();
file.get();
file.set(['line 1', 'line 2', 'line 3']);
file.add('new');
file.add(['new', 'lines']);
file.remove('new');
file.remove(['new', 'lines']);
file.save();
file.delete();
Example:
lines('.eslintignore')
.add('node_modules')
.save();
Markdown
Note: use template
function to create Markdown files.
API:
const { markdown } = require('mrm-core');
const file = markdown('file name');
file.exists();
file.get();
file.addBadge('image URL', 'link URL', 'alt text');
file.save();
file.delete();
Example:
const name = 'pizza';
markdown('Readme.md')
.addBadge(
`https://travis-ci.org/${config('github')}/${name}.svg`,
`https://travis-ci.org/${config('github')}/${name}`,
'Build Status'
)
.save();
Plain text templates
Templates use ECMAScript template literals syntax.
API:
const { template } = require('mrm-core');
const file = template('file name', 'template file name');
file.exists();
file.get();
file.apply({ key: 'value' });
file.save();
file.delete();
Example:
template('License.md', path.join(__dirname, 'License.md'))
.apply(config(), {
year: new Date().getFullYear()
})
.save();
Template:
The MIT License
===============
Copyright ${year} ${name} (${url}), contributors
Permission is hereby granted, free of charge, to any person obtaining...
Special files
package.json
API:
const { packageJson } = require('mrm-core');
const file = packageJson({ default: 'values' });
file.exists();
file.get();
file.getScript('test');
file.getScript('test', 'eslint');
file.setScript('test', 'eslint --fix');
file.appendScript('test', 'eslint --fix');
file.prependScript('test', 'eslint --fix');
file.removeScript('test');
file.removeScript(/^mocha|ava$/);
file.removeScript('test', /b/);
file.save();
file.delete();
Note: subcommand is a command between &&
in an npm script. For example, prettier --write '**/*.js' && eslint . --fix
has two subcommands: prettier…
and eslint…
.
Example:
packageJson()
.appendScript('lint', 'eslint . --ext .js --fix')
.save();
File system helpers
const { copyFiles, deleteFiles, makeDirs } = require('mrm-core');
copyFiles('source dir', 'file name');
copyFiles('source dir', ['file name 1', 'file name 2']);
copyFiles('source dir', 'file name', { overwrite: false });
deleteFiles('file name 1');
deleteFiles(['file name 1', 'folder name 1']);
makeDirs('dir name');
makeDirs(['dir name 1', 'dir name 2']);
Install and uninstall npm or Yarn packages
Installs npm package(s) and saves them to package.json
if they aren’t installed yet or not satisfying range.
const { install } = require('mrm-core');
install('eslint');
install(['tamia', 'lodash'], { dev: false });
install({ lodash: '^4.17.3' });
install(['lodash'], {
versions: { lodash: '^4.17.3', other: '1.0.0' }
});
install(['github/repo']);
Note: Works with all semver ranges, like 1.2.3
, ^1.2.0
or >=2
.
Uninstalls npm package(s) and removes them from package.json
:
const { uninstall } = require('mrm-core');
uninstall('eslint');
uninstall(['tamia', 'lodash'], { dev: false });
To use Yarn pass yarn: true
:
const { install, uninstall } = require('mrm-core');
uninstall(['eslint'], { yarn: true });
install(['standard'], { yarn: true });
Utilities
Infers style (indentation, new line at the end of file) from a source code or reads from the .editorconfig
file.
const {
inferStyle,
getStyleForFile,
getIndent,
format
} = require('mrm-core');
inferStyle('for (;;) {\n alert(1);\n}\n');
getStyleForFile('test.js');
getIndent({ indent_style: 'space', indent_size: 2 });
format('alert(1)\n', { insert_final_newline: false });
Get file extensions list from a command like eslint . --fix --ext .js,.jsx
:
const { getExtsFromCommand } = require('mrm-core');
getExtsFromCommand(`eslint . --fix --ext .js,.jsx`, 'ext');
getExtsFromCommand(`prettier --write '**/*.js'`);
Custom error class: MrmError
Use this class to notify user about expected errors in your tasks. It will be printed without a stack trace and will abort task.
const { MrmError } = require('mrm-core');
if (!fs.existsSync('.travis.yml')) {
throw new MrmError('Run travis task first');
}
Changelog
The changelog can be found on the Releases page.
Contributing
Everyone is welcome to contribute. Please take a moment to review the contributing guidelines.
Authors and license
Artem Sapegin and contributors.
MIT License, see the included License.md file.