Nocturnal Practitioners of Magic

    mrm-core
    TypeScript icon, indicating that this package has built-in type declarations

    6.1.5 • Public • Published

    mrm-core

    npm

    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}`];
    
      // .eslintrc
      const eslintrc = json('.eslintrc');
      if (!eslintrc.get('extends').startsWith(preset)) {
        eslintrc.set('extends', preset).save();
      }
    
      // .eslintignore
      lines('.eslintignore')
        .add('node_modules')
        .save();
    
      // package.json
      const pkg = packageJson()
        .setScript('lint', 'eslint . --fix')
        .setScript('pretest', 'npm run line')
        .save();
    
      // Install dependencies
      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 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 exists?
    file.get(); // Return everything
    file.get('key.subkey', 'default value'); // Return value with given address
    file.set('key.subkey', 'value'); // Set value by given address
    file.set({ key: value }); // Replace JSON with given object
    file.unset('key.subkey'); // Remove value by given address
    file.merge({ key: value }); // Merge JSON with given object
    file.save(); // Save file
    file.delete(); // Delete file

    Example:

    json('.eslintrc')
      .merge({
        extends: 'eslint-config-recommended'
      })
      .save();

    YAML

    API:

    const { yaml } = require('mrm-core');
    const file = yaml(
      'file name', // File name
      { default: 'values' }, // Default value
      { version: '1.2' } // Options
    );
    file.exists(); // File exists?
    file.get(); // Return everything
    file.get('key.subkey', 'default value'); // Return value with given address
    file.set('key.subkey', 'value'); // Set value by given address
    file.set({ key: value }); // Replace JSON with given object
    file.unset('key.subkey'); // Remove value by given address
    file.merge({ key: value }); // Merge JSON with given object
    file.save(); // Save file
    file.delete(); // Delete file

    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 exists?
    file.get(); // Return everything
    file.get('section name'); // Return section value
    file.set('section name', { key: value }); // Set section value
    file.unset('section name'); // Remove section
    file.save(); // Save file
    file.save({ withSpaces: false }); // Disable spaces around =
    file.delete(); // Delete file

    Example:

    const { ini } = require('mrm-core');
    ini('.editorconfig', 'editorconfig.org')
      .set('_global', { root: true })
      .set('*', {
        indent_style: 'tab',
        end_of_line: 'lf'
      })
      .save();

    Result:

    # editorconfig.org
    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 exists?
    file.get(); // Return everything
    file.set(['line 1', 'line 2', 'line 3']); // Set file lines, overwrite existing
    file.add('new'); // Add new line
    file.add(['new', 'lines']); // Add multiple news lines
    file.remove('new'); // Remove line
    file.remove(['new', 'lines']); // Remove multiple lines
    file.save(); // Save file
    file.delete(); // Delete file

    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 exists?
    file.get(); // Return file content
    // Add a badge at the beginning of the file (below header)
    file.addBadge('image URL', 'link URL', 'alt text');
    // Remove a badge when the predicate function returns true
    file.removeBadge(({ imageUrl, linkUrl, altText }) =>
      imageUrl.startsWith('https://travis-ci.org')
    );
    file.save(); // Save file
    file.delete(); // Delete file

    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 exists?
    file.get(); // Return file content
    file.apply({ key: 'value' }); // Replace template tags with given values
    file.save(); // Save file
    file.delete(); // Delete file

    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 exists?
    file.get(); // Return everything
    file.getScript('test'); // Return script
    file.getScript('test', 'eslint'); // Return a subcommand of a script
    file.setScript('test', 'eslint --fix'); // Replace a script with a command: a -> b
    file.appendScript('test', 'eslint --fix'); // Append command to a script: a -> a && b
    file.prependScript('test', 'eslint --fix'); // Prepend a script with a command: a -> b && a
    file.removeScript('test'); // Remove script
    file.removeScript(/^mocha|ava$/); // Remove all scripts that match a regexp
    file.removeScript('test', /b/); // Remove subcommands from a script: a && b -> a
    file.save(); // Save file
    file.delete(); // Delete file
    // All methods of json() work too

    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'); // Copy file
    copyFiles('source dir', ['file name 1', 'file name 2']); // Copy files
    copyFiles('source dir', 'file name', { overwrite: false }); // Do not overwrite
    deleteFiles('file name 1'); // Delete file or folder
    deleteFiles(['file name 1', 'folder name 1']); // Delete files or folders
    makeDirs('dir name'); // Create folder
    makeDirs(['dir name 1', 'dir name 2']); // Create folders

    Install and uninstall npm, Yarn, or pnpm 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 to devDependencies
    install(['tamia', 'lodash'], { dev: false }); // Install to dependencies
    install({ lodash: '^4.17.3' }); // Install particular version
    install(['lodash'], {
      versions: { lodash: '^4.17.3', other: '1.0.0' }
    }); // Install particular version
    install(['github/repo']); // Install non-registry package without version

    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 from devDependencies
    uninstall(['tamia', 'lodash'], { dev: false }); // Uninstall from dependencies

    To use Yarn pass yarn: true:

    const { install, uninstall } = require('mrm-core');
    
    uninstall(['eslint'], { yarn: true });
    install(['standard'], { yarn: true });

    With Yarn Berry, pass yarnBerry: true and for pnpm, pass pnpm: 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');
    // => { insert_final_newline: true, indent_style: 'space', indent_size: 2 }
    getStyleForFile('test.js');
    // => { insert_final_newline: false, indent_style: 'tab', indent_size: 'tab' }
    getIndent({ indent_style: 'space', indent_size: 2 });
    // => '  '
    format('alert(1)\n', { insert_final_newline: false });
    // => 'alert(1)'
    // Only insert_final_newline is supported

    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');
    // => ['js', 'jsx']
    getExtsFromCommand(`prettier --write '**/*.js'`);
    // => ['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.

    Install

    npm i mrm-core

    DownloadsWeekly Downloads

    19,666

    Version

    6.1.5

    License

    MIT

    Unpacked Size

    121 kB

    Total Files

    50

    Last publish

    Collaborators

    • avatar