A Program that turns pretty formatted MUSHCode into something you can quote to your client, mixed with a few extras to speed up your coding flow.
npm i mu-format
There are just a few steps in setting up the formatter.
// Create your app
const Formatter = require('mu-formatter');
const app = new Formatter({
plugins: [
'./extras/plugins/plugin1',
'./another/place/plugin2'
]
});
// Setup some event listeners
app.on('log', log => console.log(log));
app.on('error', error => app.logger(error.stack));
app.on('done', results => console.log(results[0].contents));
// Now run the formatter! If you run a file from the directory
// level, or from a github repo, it will look for a file called
// `installer.mu`.
app.format('./code/codefile.mu'); // or
app.format('./code/'); // or
app.format('github:user/repo');
At it's heart, Mu-Format is a middleware driven program that runs lists, or queues of functions, or jobs. The majority of the work happens in in the render (or pre-render) and compress (or pre-compress) queues.
Each job is passed a data
object, containing information about the document currently, as well as a few helper functions to trigger formatter events.
//Some functionality removed for simplification.
const data = {
path: './code/installer.mu',
baseDir: './code/',
// The working text of the format file.
// All of the #includes are combined into this
// attribute, and modify it through out the format
// process.
txt: '...',
// A copy of the composited document before rendering
// and compressing.
raw: '...',
// vars is a unified place to save working data.
vars: {
headers: [
{
name: 'Foo',
value: 'bar'
},
{
name: 'Something',
value: 'important'
}
]
},
// The options passed to mu-format at instantiation
options: {
plugins: [
'plugin1',
'/path/to/plugin2'
]
}
}
data.emit('Event',[data]) Trigger a custom event on the formatter object.
data.log(message) Trigger a 'log' event on the formatter object, and also save the message in the result object log.
data.error(error) Trigger an 'error' event on theformatter object. Error must be an error object.
module.exports = app => {
// First, create a queue. When you call the queue command,
// it will either create the queue if it doesn't exist - or
// load an existing queue object. We can link our methods
// through chaining. The job function is passed a single
// parameter, data.
app.queue('render')
.addJob('someJob', data => {
// actions on the data object. Normally you'll be working
// on data.txt where the document contents are stored
// - but there are other things the data object can do.
data.log('Log message!')
})
}
// to run a single job from a queue:
app.queue('name').job('jobName')(data)
Creating a plugin for the system is pretty straight forward. Make a module that exports a function.
/* .plugins/plugin.js */
module.exports = app => {
app.queue('pre-render')
.addJob('custom-job' data => {
// Do whatever processing you need to do on the current
// document (data.txt).
})
}
/* index.js */
// after your app is declared.
app.plugins(['./plugins/plugin.js'])
// Or you can declare plugins at runtime.
app.format('github:user/repo',{
plugins:['./plugins/plugin.js']
})
// You can even just require the file:
require('./plugins/plugin.js')(app)
The rules for formatting .mu documents is pretty simple! First, You can format your code however you'd like. I suggest adopting an easy to read style using indentations and spacing to make your code digestable. MU-Format looks for [&+@-]
in the first position of the current line to designate the start of a new command, attribute, or spacer. You can add comments in your code in either /* ... */
block style or //
inline style comments.
A spacer, or dash -
is a purely cosmetic mark for formatting the program output. During the compression phase blank lines are removed. Dashes become newlines making the processed code a little easier to process through.
/*
------------------------------------------
--- Commands: +things & +stuff -----------
*/
@@ A comment for someone reading your minified code.
-
&cmd.mycommand me = $+Stuff *:
@pemit %#=You entered things and %0.
// Another command that does things
&cmd.mycommandtwo me = $+things *=*:
// Sets an attribute on myself!
&_things me=%0-%1;
think Things %0 %1! // Wut?
When we format this block of code, it turns into:
@@ A comment for someone reading your minified code.
&cmd_mycommand me = $+Stuff *: @pemit %#=You entered things and %0.
&cmd_mycommandtwo me = $+things *=*: &_things me=%0-%1; think Things %0 %1!
Simple!
The real power of Mu-Format comes in it's #meta tag system. #metas are, when the code is processed, translated into MU friendly output. They are honestly a good way to save a few keystrokes and even organize your code projects. Lets take a look at the few that exist right now:
this #meta allows you to import a file (or entire Github repository) into the current file. #include accepts three kinds of files right now:
- Local File
You can designate a local file to include, entering the
./path/to/file.mu
format. - Local Directory
If you list a directory, Mu-Format will look for a file called
installer.mu
and kick off the #include from there. - Github Archive
This is the same as installing from a local directory, instead you'll you'll enter
github:user/repo
. If you start hitting errors while compiling from Github, try adding Github authorization when you create a new Formatter object.
app = new Formatter({
gitUser: 'user',
gitPass: '123Secret!'
})
Example
&cmd.command #123 = $foo *:
think me Foo %0.
// Include the rest of the library.
#include ./path/to/file2.mu
Honestly #esc (or #file) works list like #include, except it escapes each line of text with a MUSH null string @@
so they don't get quoted to the Game. This is great for things like license files, and other custom comments text.
@@ Legal Stuff
@@ Bla bla, yadda
@@
@@ Instructions
@@ ----------------
@@ 1. Things and
@@ 2. Stuff
Add key/value information to be listed at the very top or bottom of the resulting file.
#header version=1.0.0
escapes into: @@ version 1.0.0
at the top of the resulting document.
Roll your own edits! I wanted to keep the system as open to modification as I could. #def
#metas are are a way to replace tags with code at processing time. I find them really useful for code snippets that I'm going to use more than once or twice. Remember! a @&-+
at the beginning of a line results in a new command or attribute being defined.
#def #check-wiz
@assert hasflag(%#,Wizard) = {@pemit %#=Permission denied.}
#enddef
Then later in your code:
&cmd.wizcode #1234=$+cmd foobar:
#wiz-check
// Rest of your code ...
You can also make a #def
that uses a regular expression string (you don't need to provide the beginning and end of the search //
). Any group matches can represented in your code in the variables $0 - $9
Remember! $0
is the entire match.
#def #create\s+(.*)\s*=\s*(.*)
@if [locate(me,$1,*)] = {
@create $1;
&$2 me = [locate(me,$1,*)];
},{
&$2 me = [locate(me,$1,*)];
}
#enddef