Giter VIP home page Giter VIP logo

mrm's Introduction

Mrm

npm Codecov Node.js CI status

Command line tool to help you keep configuration (package.json, .gitignore, .eslintrc, etc.) of your open source projects in sync.

Features

  • Doesn’t overwrite your data unless you want to
  • Minimal changes: keeps the original file formatting or read the style from EditorConfig
  • Minimal configuration: tries to infer configuration from the project itself or from the environment
  • Customizable tasks for popular tools like ESLint, Prettier, lint-staged, etc. included
  • Custom tasks and tools to work with JSON, YAML, INI, Markdown and new line separated text files
  • Sharing tasks via npm and grouping them into presets

Motivation

Most of the available tools are template based. Template approach works moderately well for new project generation but doesn’t work well for updating. Mrm’s approach is closer to codemods than templates.

Read more in my article, Automating open source project configuration with Mrm, or watch my talk on Mrm.

Documentation

Tasks

These tasks are included by default:

Changelog

The changelog can be found on the Releases page.

Sponsoring

This software has been developed with lots of coffee, buy me one more cup to keep it going.

Buy Me A Coffee

Contributing

Bug fixes are welcome, but not new features. Please take a moment to review the contributing guidelines.

Authors and license

Artem Sapegin and contributors.

MIT License, see the included License.md file.

mrm's People

Contributors

0x2206 avatar ai avatar aleung avatar bebraw avatar betaorbust avatar brettz9 avatar burtharris avatar d2s avatar dependabot[bot] avatar ext avatar fengzilong avatar filipekiss avatar frontsideair avatar gavvvr avatar glensc avatar igorkamyshev avatar ininit avatar joewhite avatar kinday avatar lucasconstantino avatar lukeed avatar matt-tingen avatar nperez0111 avatar okonet avatar saibotsivad avatar sapegin avatar selim13 avatar thetutlage avatar vctr-dev avatar voloshchenkoal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mrm's Issues

Allowing async tasks

Currently all tasks has to be synchronous and hence using modules like https://github.com/SBoudrias/Inquirer.js is impossible.

I am happy to work on adding the async tasks, but wanted to know if you are interested in same?

If yes, I will share the docs around async API and then start working on the PR.

New major release: 3.0.0

There are several significant changes in this release:

  • Tasks and preset autoinstall #99
  • Deprecate the default preset and remove it as a dependency from the mrm package in favor of autoinstall #136
  • Migrate all tasks to the new config API, based on Inquirer (there's one task left — gitter) #137
  • Remove the old config API #139
  • Drop Node.js 8 support #138
  • Revise docs

Setting `yarn` as the default

OK, this may be another easy one I'm missing, but here goes: Assume you have a new project with this as your ~/.mrm/config.json:

{
    "yarn": true,
    "aliases": {
        "default": ["package", "prettier", "typescript", "eslint"]
    }
}

If you run mrm default, prettier begins installing using npm.

It seems like getTaskOptions in mrm/packages/mrm/src/index.js is only using the default options if there are no parameters to the task.

Or, should mrm/packages/mrm-core/src/npm.js re-read the global config.json to get the yarn flag?

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here is some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


Invalid npm token.

The npm token configured in the NPM_TOKEN environment variable must be a valid token allowing to publish to the registry https://registry.npmjs.org/.

If you are using Two-Factor Authentication, make configure the auth-only level is supported. semantic-release cannot publish with the default auth-and-writes level.

Please make sure to set the NPM_TOKEN environment variable in your CI with the exact value of the npm token.


Good luck with your project ✨

Your semantic-release bot 📦🚀

More ESLint properties

Hi,

Would you be open to a PR to add the remaining standard ESLint properties (of your config type):
"env", "globals", "ignorePatterns", "noInlineConfig", "overrides", "parser", "parserOptions", "plugins", "root", "settings"

I suppose they could either be separate options, or just all part of one extra eslintConfig object.

I'd also like to see about adding support for comma-separated eslintPreset (for extends).

Run multiple tasks

Right now you can run only one tasks at a time, would be cool to run multiple tasks from a command line like this:

mrm <task1> <task2> <task3>

Autoinstall tasks

Hi,

I've created a task mrm-task-yarnhook to make installation of yarnhook a one-liner. (Awesome project by the way!) But when I try to see if it works, I'm getting a confusing error message.

Task “yarnhook” not found.

We’ve tried these locations:

- /Users/fatih/dotfiles/mrm/yarnhook/index.js
- /Users/fatih/.mrm/yarnhook/index.js
- “yarnhook” in the default mrm tasks
- npm install -g mrm-task-yarnhook
- npm install -g yarnhook

Reading the sharing tasks doc page, I would expect mrm to find mrm-task-yarnhook automatically. It seems like this task must be included in default tasks or must be globally installed, which would make installation more than one line.

I wonder if mrm can automatically find the task using npm, which would be very useful. Otherwise it would be nice to update the documentation to make this clear. In that case, would it be okay to add mrm-task-yarnhook to the default task set?

Best

Use preset with npx

Is it possible to use a preset with npx?
Or do I need to install both mrm and the preset globally?

Read configuration from project directory

First introduce my use case:

My team develops a system in micro-service architecture which have several node.js projects. I'm trying to use mrm to unify configuration of all projects. I have defined a bunch of tasks according to our design guidelines. But not all configurations are common to all projects. Some configurations have to be set per-project. And also, some tasks should only be applied on part of projects.

I think if mrm would resolve configuration from .mrm file under project root directory, in which not only contains config items specific to current project but also defines which tasks from which preset should be applied, then I can run mrm without any argument inside each project to update its configuration according to latest version of preset/tasks published in our private npm registry.

It would be helpful for a big team to manage projects to keep them up-to-date with team's project template/configuration.

Accept package versions in install

Something like this:

install({
  jest: '^21.0.0'
})

It should accept a version range and should only update package.json if the current range doesn’t satisfy a new one.

Idea: Built-in task to install third-party tasks

Hi,

While I see you describe a mechanism for publishing one's own tasks and presets, how about an mrm command to make their discovery even easier by performing an install of such tasks from within mrm?

  • mrm i my-task - runs npm i mrm-task-my-task
  • mrm i my-task -h - effectively performs npm i mrm-task-my-task but with the package and its dependencies moved into ~/.mrm/my-task/
  • mrm i my-task -d - as with -h, but into ~/dotfiles/mrm
  • mrm i my-task -g - runs npm i -g mrm-task-my-task

(Alternatively -h might be the default, and -l would indicate a local install.)

Ideally another flag (or separate task) could not only install them but also invoke them, e.g.:

  • mrm i my-task -hx

Or even better, a main mrm flag could indicate the user wants a task to first be installed from npm if no built-in one of that name is found, and then execute the task once installed:

  • mrm my-task --npm -h

or just:

  • mrm my-task --nh

I think this would really suggest to newcomers that the project had really embraced third-party tasks (and is not an overly opinionated tool).

Tests for tasks

I would like to contribute to tasks but without tests it is very fragile ATM. Do you think there is an easy way to add tests for them.

My concern is that since tasks are basically a set of side-effects it is tricky to write tests for them. If the task modifies a file, it is rather simple to add snapshot tests for those but this will still require to mock the real implementation or introduce a way to work with temp files.

Ideally, I think, it would be great to get test utilities for each "transformation" / "task" but I can imagine this is a big effort.

PS: This is more a conversation starter.

customize commands

So I have encountered an interesting issue while trying to run a task and I have no idea how to name it or even what to ask for. I guess I will just leave it here for your consideration.

The problem is that I am using yarn workspaces and in order to install something to root, I must explicitly call yarn with -W flag.

$ npx mrm lint-staged
npx: installed 173 in 14.685s
Running lint-staged...
Update package.json
Installing lint-staged...
error Running this command will add the dependency to the workspace root rather than the workspace itself, which might not be what you want - if you really meant it, make it explicit by running this command again with the -W flag (or --ignore-workspace-root-check).

Maybe perhaps if I could set an alias for yarn to yarn -W temporarily..? No idea what would be the best solution, but I think cases like this may happen not only with yarn.

Interactive mode - tasks status

Interactive mode - tasks status

This issue is a follow up of current status for core tasks implementation of interactive config mode, discussed in #51 and introduced on #53:

  • codecov in progress
  • contributing
  • editorconfig
  • eslint
  • gitignore
  • gitter
  • jest
  • license
  • lint-staged
  • package
  • prettier
  • readme
  • semantic-release
  • styleguidist
  • stylelint
  • travis
  • typescript

Automatic version appending during install breaks github dependencies.

Issue

Passing a github or other supported services ((npm)[], (yarn)[]) through Mrm's install causes the installer to throw an error. This appears to be because the service-based installs do not support versioning, and we append a @${version} to every attempted install.

function getVersionedDep(dep, versions) {
const version = versions[dep] || 'latest';
return `${dep}@${version}`;
}

Proposed solutions

  1. Seamless but hard: Detect and non-registry installs to mirror NPM/Yarn's capabilities.
  2. Expands API, but easy: Add an escape hatch (perhaps passing null as the version in an explicit version config, to signal leaving off the @${version} during the install call?

Decouple mrm and mrm-preset-default

  • mrm package rarely changes, so it'll make preset updates more clear.
  • Not everyone is interested in the default preset, it won't pollute he list of tasks you actually need and use.

Presets

Preset is an npm package that provides a config and tasks that you could run like this:

mrm update --preset pizza

Where update is a task or alias name inside an mrm-preset-pizza npm package.

That’s will simplify sharing between projects similar to webpack-defaults but without a need to write a custom task runner.

Allow checking on CI

It would be helpful if mrm allowed for a --check command, like prettier does. The command should exit with an error if the current project files are not up-to-date with the config (so if there should be any changes made to the project files).

That way, a user can:

  1. set their preferred mrm preset as a dependency for their project
  2. add a script to check on CI: "mrm:check": "mrm gitignore license --check --preset my-preset"

That way, they'll be notified by a failing build/test if their project is not up to date with their mrm settings. It'll allow for easier updating with tools like greenkeeper as well.

Load tasks from npm

One possible way to do that is by a name convention like mrm-task-pizza. Mrm will try to load a pizza task locally as it does now and if it’s not found will try to require('mrm-task-pizza').

We should also make it work with scoped packages.

Package lock updates

With npm 7 now available, running install gives a very different set of package-lock's. If possible, it'd be nice to be submit a PR which cleans up that noise if you're ok using npm 7.

Or, even more ideal from my perspective if you're ok with it, switching to pnpm to save on disk space. But if not, it'd be nice to be able to commit the package-lock updates.

.js files

Just wanted to see what you think about .js files, say I want to install Webpack, it comes with webpack.config.js.

Will you parse the JS files and modify their AST, with esprima and such?

Could cascade aliases

This feature may help write multip alias.

  "aliases": {
    "common": ["license", "readme", "editorconfig", "gitignore"],
    "node": ["common", "npm"],
    "frontend": ["node", "eslint"],
    "backend": ["node", "foo"]
  }

I see in runAlias before runTask not judge task is a alias, may be should do it.

No template for the "undefined" found, skipping

I've followed the installation instructions and added following config to ~/.mrm/config.json

{
    "name": "Andrey Okonetchnikov",
    "email": "[email protected]",
    "url": "http://okonet.ru",
    "github": "okonet",
    "indent": "2",
    "readme": "README",
    "license": "LICENSE",
    "aliases": { 
        "node": ["license", "readme", "package", "editorconfig", "eslint", "gitignore"]
    }
}

but then I run a command I see

➜ mrm license                                      
Running license...
No template for the "undefined" found, skipping

Interactive mode is not clear

I spent some time trying to figure out how to make inquirer prompt my parameters with no success, until I dive into the code to see that is expecting a --interactive flag. I feel that's not very intuitive since I am defining parameters in my task I feel it should work by default.

Also it could be listing in the docs that you have to explicitly pass --interactive to get the prompt.

Happy to help to improve this tool, it's awesome and have a lot of potential!

What are `aliases`?

I hate to ask what seems to be a simple question, but what are aliases?
How are they defined?

Add option to set yaml spec version

In mrm-core the yaml API by default reads and writes yaml file in 1.2 spec. However, sometime we need the file in yaml 1.1 spec. A specific example is that yes is string in yaml 1.2 but docker-compose still consider it a boolean as in yaml 1.1.

It could be solved by adding an option in yaml API to specify the yaml spec version. The underlay yaml dependency already provide the version option.

I had tried to update mrm-core, but I found difficult that the yaml API doesn't reserve an option argument and there is already an optional argument defaultValue. If I append an options to the argument list, then defaultValue must be provided in order to set options.

yaml(filename, defaultValues)                 // current

yaml(filename, defaultValues, options)        // `defaultVaules` becomes mandatory to set `options`

yaml(filename, {defaultValues, yamlVersion})  // make `defaultValues` a property of the `options`
                                              // it breaks API compatibility

Sharing tasks (or more appropriately .mrm settings)

So, let's say I've updated quite a few tasks to fit our defaults. My ~/.mrm folder is configured with the ~/.mrm/config.json with tasks I'd like to share, and our specific take on things like mrm-task-eslint, etc.

Some of the changes are small, such as jest has the --passWithNoTests field added to the scripts.

However, some are huge, like eslint -- we have a lot of custom rules/additional packages we need to install.

What's the best way to handle this?

Use GH Actions for semantic-release

Some of my Travis CI integrations were deprecated and won't run anymore (not sure who's fault, looks like GH) but I'm wondering if semantic-release should use a GH action instead to avoid breakages (I hope as long it's on GH infra it will run).

About embracing the command line for interactive configuration

I've being using this project for little time, and I think it's configuration pattern is really amazing for most cases. Being able to define global wise config setups that you often use is very handy. But, the current command line way of providing/overriding configs is, in my perspective, very annoying. Either with configuration files or command line parameters, someone that's using mrm for the first time for, say, configuring husky will need to read trough the related task to be able to determine if the outcome is what he expects, and only then execute npx mrm lint-staged with the proper config inline or set in some configuration file.

My point is I think this whole project is not far from being able to benefit from proper interactive command line to allow the user, with a single command and with no previous reading of documentations, to configure specifics to each task with a single action.

This could definitely be opt-in, as for the current expectation of things. This could even benefit from defaults (based on config files or command line) to make the process faster and simpler for those more advanced users.

So, my initial idea for that would be something similar to that:

$ npx mrm -i lint-staged

Due to the interactive mode set to on, this script would then ask interactive for the each option defined in the lint-staged task, fulfilling defaults by respecting global config files (or even command line parameters). We could use a prompting CLI tool such as enquirer for that, and we could benefit from enquirer prompt types as a pattern for each task module to define it's configs as well, giving proper capabilities to each task to define how each of their configs might be configurable (text input, multiple choice, list, etc).

I'm already up to do that implementation using a simple mrm task as an example. We could and should definitely be retro-compatible in the outcome, and as far as I looked into tasks code so far, there is currently no way we could infer config types so that we would only need changing the core packages - that means there would be some slight changes to be done in each task, but those tasks that don't implement it should work the same, but with no interactive configuration.

Any thoughts?

Task hooks/events

General idea

Allow tasks to define which specific events they emit, and then perform the emitting within the main task.

Allow this task or other tasks to have special subtasks listening for such events, and when there is a match, the subtask is executed. The subtask is similar to a regular task except that:

  1. It probably should not be allowed to define its own subtasks
  2. It can return a value which can be collected by the executing task.

The emitting task will begin execution but upon encountering a call to execute hooks, will wait for all identified subtasks to execute (including their prompts), and then can use their return values to determine an order for applying the results.

Motivation

Sometimes tasks interact in ways where it is desirable that a single task allows consumers and can be provided by its consumers with information that it can use to determine how to present.

Alternatives considered

This is similar in some regards to the notion of hooks in #52, but:

  1. It is intended more for tasks to define events rather than mrm-core to do so.
  2. It attempts to allow subtasks whose results are collected and submitted to the hook registrant, allowing the registrant to apply the hooks in the manner/order deemed fit, potentially using the subtask return results to determine a suitable order or presentation.

So, as opposed to my understanding of #52, this particular notion of hooks (or events) in this issue is not for a hook that is a convenient way to override an existing core behavior on a single per-task basis.

This approach could conceivably be applied to mrm-core to override core code behavior, allowing core itself to register events which tasks could use. However, since in this proposal we are collecting results from multiple tasks before applying, core events would likely not be suitable since different tasks would not necessarily want overrides applied (e.g., one task might want to transform JSON and another would not). It would I think be a better separation in most cases for a single task to manage responsibility. This would also offer the advantage that projects do not need to wait for changes to core to make extensible, interacting behaviors.

Sample use cases

  1. Allowing users of the readme task to present a single reusable template in config which, while referencing all potential variables for where other tasks' badges might be inserted (e.g., for linting, testing, coverage, license info, etc.), can also be used regardless of the tasks currently in use.
${npmBadge && npmBadge + '\n\n'}${testBadge || coverageBadge ? testBadge + coverageBadge + '\n\n'}${licenseBadge && licenseBadge + '\n\n'}## Usage

And they won't need to create the README in this manner first; the template would be able to create it in this sequence from the beginning.

  1. When specifying extensions for use by ESLint, one might prefer the extensions be assembled in a particular order:
eslint --ext=js,ts,md,html

rather than

eslint --ext=html,js,md,ts

Tasks like a Markdown or README task, could offer "md" as an extension, a TypeScript task could offer "ts", etc., and then the eslint task could provide a template or otherwise assemble the extensions in a task-specifiable order.

And as with the previous example, there is no need for the user to edit the package.json ahead of time to specify the desired order; their task template could specify this ahead of time and be reused across project.

Proposed API

// Main task signature
module.exports = (userConfig, argv, executeHooks) => {
  // This will invoke the hook with user config, argv, and hookConfig
  // Note that `returnResults` is an array, e.g., of strings
  const returnResults = executeHooks('readme:add', hookConfig);
  // Use results...

};
module.exports = ['readme:add'];

// A task can both register hooks and add its own, as well as listen to its own events
mainTask.listens = {/* ... */}; // See below

// In another task
// Accept hook config object separate from user config (and argv) so that executor can pass its own
const readmeAddSubtask = (userConfig, argv, hookConfig) => {};

// Indicate the used hooks
module.exports = task;
task.listens = {
    'readme:add': readmeAddSubtask
};

Sketch of control flow

(Steps are in series.)

  1. Define a map allCollectedOptions
  2. For each task task:
    1. For each of a task's emitsevents, perform these steps:
      1. Let the individual emits event be called event
      2. For each of all other tasks besides task:
        1. Let the individual task being iterated be called checkTask
        2. Throw if checkTask is found to register itself for defining an event of the same name as event.
    2. Begin executing the main module for task
    3. Define an empty map executedEventsToOptions.
    4. If, during execution, task calls executeHooks, then pause execution and follow these steps:
      1. Let hookEvent be the name of the event with which executeHooks is invoked
      2. Let hookConfig be any additional config passed (as the second argument of executeHooks).
      3. Define an empty array results.
      4. For each of all tasks (including task):
        1. Let the individual task being iterated be called checkTask
        2. If listens on checkTask is found with a hook subtask that matches hookEvent:
          1. Let the individual matching subtask be called subtask
          2. Look up hookEvent on the executedEventsToOptions map.
          3. If a value is present in the map, assign it to collectedOptions
          4. Otherwise:
            1. Create an empty map collectedOptions.
            2. For each option option of subtask:
              1. If the CLI is configured to run in interactive mode and option is not present in allCollectedOptions, obtain option interactively as value.
              2. Otherwise, obtain its value as value.
              3. Set option as the key on collectedOptions with its value as value
              4. If not present on allCollectedOptions, set option as the key on allCollectedOptions with its value as value.
            3. Add collectedOptions to executedEventsToOptions with hookEvent.
          5. Execute subtask with collectedOptions, CLI arguments, and hookConfig and assign the result to result.
          6. Add result to results.
      5. Add hookEvent to executedEvents.
      6. Supply results as the return result of executeHooks and resume execution of task
    5. Repeat step 3 so long as task is not complete.

UnhandledPromiseRejectionWarning: MrmError

When MrmError is thrown from a task, got 'UnhandledPromiseRejectionWarning: MrmError: ...' on console and mrm cli doesn't exit with error.

The issue is introduced by #30 since v1.2.0.

Automatic global install for tasks

Currently, any custom (not part of core repository) Mrm task must be first installed globally in order to be used - which contradicts the principle of non-polluting the host machine, carried out very well by the npx mrm approach. Similar libraries have found ways to address this, and carry out search for the mentioned derivate package either by name patter or published keywords. I think Mrm should be able to do the same.

Thoughts?

Support scoped presets and tasks

I came across mrm by chance and I love the idea of having "programmable configurations". Great job!

I've been trying to build a preset to share with my team, but we're using Github Registry so all the packages must be scoped.

It would be great if mrm could support scoped packages like @myorg/mrm-preset-default by just calling mrm license --preset @myorg/default. The same would work for tasks, so mrm @myorg/custom-task would look for @myorg/mrm-task-custom-task.

Yarn supports this, for example yarn create @myorg/package will look for @myorg/create-package.

I'm willing to give it a go with a pull-request if that's something you're interested in.

Thanks again for the great tool!

use `npx mrm lint-staged` but `Cannot configure lint-staged`

I was trying to use create-react-app create a react-demo with add eslint & prettier config in the package.json. When I run the command npx mrm lint-staged failed and show error as Cannot configure lint-staged, only ESLint, stylelint or custom rules are supported.

After checking the code, I found there is an if statement pkg.get('devDependencies.prettier') && !pkg.get('devDependencies.eslint-plugin-prettier'). In my case, I have both prettier and eslint-plugin-prettier installed, which casued a failure.

Does anyone have the same issue or have a solution, thanks

  • OS : MacOS

  • my package.json config:

"devDependencies": {
    "eslint-config-airbnb": "^18.0.1",
    "eslint-config-prettier": "^6.4.0",
    "eslint-plugin-prettier": "^3.1.1",
    "node-sass": "^4.12.0",
    "prettier": "^1.18.2"
  }

New line being removed

When running tasks I noticed that often they also remove the new line /n at the end which is required by my .editorconfig.

2017-09-05 at 10 04

What exactly is this for?

I read the README, but I'm not sure what I'd actually use mrm for.

It is similar to Builder? Or more like Projector or Gulp for just running tasks?

How does mrm relate to multiple repos or multiple projects?

In particular, I'm looking to make a monorepo that has packages that I publish to NPM, where these packages are unrelated and they'd have their own version numbers not in sync (unlike Lerna's default locked mode that keeps versions in sync), and I'd like to share the same build steps with all the projects so that I don't have to manage confugration for each project/package. Something like Lerna would let me run the same command in all repos, but it doesn't dictate anything about how each project or package will be built or if they even share any configuration. Builder let's us run the same sort of build steps in any repo or project that prescribes to the "archetype" which means I could, f.e., share a webpack build configuration with all projects and build them all in the same way.

How/where does mrm fit into having a monorepo where we want to take advantage of the same build tools and configurations for all projects/packages, and easily update and publish some or all of these packages?

About customization/extention of commands using "hooks"

Project setup is a mess because one thing will always interferir with others: eslint my need to use babel's parsing; husky/lint-staged might need to understand there is prettier/eslint available; jest setup needs to know compilation decisions; and so on.

Taking that in consideration, and combining that with the (very nice) idea of presets mrm provides, I would like to suggest we could implement some sort of hook based capabilities for interactions between different tasks - or even global interference, eventually.

One patter I think might suit the current architecture of this project is hooks - or event based alters, for that mater.

There are many parts of the system we could benefit from hooks. For instance, we could have hooks that allow altering formats actions, such as save on JSONs:

module.exports = function(filename, defaultValues, hook) {
  return {
    /** Save file */
    save() {
      const result = hook('format:json:save', json, { filename, defaultValues })
      const content = commentsJson.stringify(result, null, file.getIndent())
      file.save(content)
      return this
    },
  }
}

This would ultimately allow modules/tasks to alter results of JSON file editing (for whatever reason that be, this is just an example):

task.hooks = {
  'format:json:save': (json, { fileName }) => fileName === 'package.json'
    ? { ....json, customField: 'value' }
    : json
}

module.export = task

This, when combining with presets (that will ultimately run multiple tasks together) could mean lot's of decoupling on current code.

This could also allow, for instance, for specific packages (or even user's global setup) to extend specific action on tasks. #48, for instance, could be solved with something similar to this:

function runYarn(deps, options, exec) {
  // ...
  const alteredArgs = hook('npm:yarn:install:args', args, { deps, options, exec })

	return execCommand(exec, 'yarn', alteredArgs, {
		stdio: options.stdio === undefined ? 'inherit' : options.stdio,
		cwd: options.cwd,
	});
}

This could be very simply benefited anywhere, like a mrm-task-yarn-workspace package or the user's global, like this:

task.hooks = {
  'npm:yarn:install:args': args => ['-W', ...args],
}
module.export = task

Disclaimer: I don't think this situation is a good example that should be put in another module. In this specific case, I agree this could be handled by the mrm-core module, as there is definitely a way to determine if we need to set the workspace flag or not. This is only an example of how easy changes like this could become even without the need of PRs or code-change, when many times the involved package might not be the desirable place to some specific customization or when there is no desire to add a configuration to solve such request.

I think if this gets considered, the implementation itself would definitely be retro-compatible, and could happen incrementally for most of the situations. Definitely there could be some code cleaning due to that, and some better separation of concerns in some packages, though that kind of refactor would have to be discussed in each particular case.

Run all tasks in a preset

I'm trying to use mrm to standardize all projects in my product line. All our file templates and configuration can be set by creating our own preset. That's fine.

But developer has to list all tasks to be executed. How about adding a command to run all tasks in a preset?

pnpm option

Hi,

First let me thank you for what looks like is going to be an incredibly useful tool.

In finding my desktop hijacked by an ever-growing number of repos/packages (something your project will likely only encourage), I am drawn to pnpm for saving space in avoiding immensely redundant storage.

Would you consider adding it as an option for installs, as you do for yarn?

I'd ideally like to see this configurable such that other tasks, like say the eslint task, would use pnpm instead of npm, but if it t least enters the API, it'd be encouraging to know I can make tasks to use it.

No module resolution for overridden tasks

I created a file ~/dotfiles/mrm/config.json and used the template provided in the Readme.

Then I made a folder ~/dotfiles/mrm/license and copied both these files into that folder.

Then I ran the command mrm and received the error: Error: Cannot find module 'mrm-core'

How are the dependencies in ~/dotfiles/mrm/* expected to be resolved? I could make ~/dotfiles/mrm/package.json and then npm install --save my dependencies, but were you expecting a different approach?

This should probably get put in the Readme file.

Idea: Inquirer plugins

Guess I do have one other idea for now.

How about allowing the user to be prompted for install of inquirer plugins for use by tasks, such as by allowing task modules to export a plugins string-of-arrays parameter which listed npm packages?

I can see uses for say an eslint package, adding the autocomplete plugin, or any number of tasks seeking the file selection plugin.

This is not a top priority for me now, but figured it'd be good to get any thoughts.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.