
Security News
Feross on the 10 Minutes or Less Podcast: Nobody Reads the Code
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.
Macronator is a macro replacement utility for easy generation of boilerplate or other repetitive code, function stubs, dummy functions and other usually boring and tedious tasks.
You can define macros in your code files for any piece of code that is repetitive in nature. The macro body should define the piece of code that is to be duplicated, with tokens for parts that are different for each duplication. For example, the example below will produce field definitions for all possible combinations of three values:
const VALUES = {};
/* MACRO.BODY */
VALUES.%KEY% = %VALUE%;
/* MACRO.BODY */
In order for the macro to work, a header must be defined (before the body) which declares two local-scope values:
TOKENS: function (range1, ...), a function that returns an array of token objects for a given set of
range valuesRANGES: array of objects, each range object contains keys and corresponding values depending on range type:
{ each: <object> }: each value in an object, produces an array of all values{ keys: <object> }: all keys in an object, produces an array of all keys (strings or numbers){ values: <array> }: explicit list of values, copies array (eg [ 0, 5, 10 ]){ from: <number>, to: <number> }: all integers between from and to, inclusiveFor example, let's combine three values into ranges (this header has to be defined before the body above).
The tokens function will receive three arguments, for instance ("min", "speed", "z") and must return the
values for the %KEY% and %VALUE% tokens.
/* MACRO.HEADER */
const RANGES = [
{ values: [ "min", "avg", "max" ] },
{ values: [ "position", "velocity", "acceleration", "momentum", "force" ] },
{ values: [ "x", "y", "z" ] }
];
const TOKENS = (r1, r2, r3) => ({
KEY: (r1 + "_" + r2 + "_" + r3).toUpperCase(),
VALUE: '{ range: "' + r1 + '", value: "' + r2 + '", axis: "' + r3 + '" }'
});
/* MACRO.HEADER */
This will generate all possible values:
const VALUES = {};
VALUES.MIN_POSITION_X = { range: "min", value: "position", axis: "x" };
VALUES.MIN_POSITION_Y = { range: "min", value: "position", axis: "y" };
VALUES.MIN_POSITION_Z = { range: "min", value: "position", axis: "z" };
VALUES.MIN_VELOCITY_X = { range: "min", value: "velocity", axis: "x" };
...
The command line interface is a very simple script that parses and processes all macros from an input file and saves the generated code (plus all the untouched code) in the output file.
node path/to/cli.js <inputFile> <outputFile>
In case the output file is not specified, the same file will be used, and a back-up will be saved alongside the input / output file.
There are two functions of interest provided in the module:
const macronator = require("macronator");
const inputRows = ...; // maybe read them from a file or via HTTP request
// extract macros from rows, macros will be returned as res.macros, the remaining code as res.code
let res = macronator.extractMacros(inputRows);
// execute the extracted macros
let outputRows = macronator.processMacros(res.macros, res.code);
For more information, there is full documentation in the documentation directory.
Macro blocks are defined using comments and keywords. Keywords are:
MACRO.HEADER: a macro header, eg /* MACRO.HEADER <name>
name: string, the name of this macro, if anyMACRO.BODY: a macro body, eg /* MACRO.BODY <name>
name: string, the name of this macro, if present, it must match name in headerMACRO.END: a single end of comment *\/ ends a macro header or body; no argumentsA macro block can be either fully commented or included between two macro comments, as such:
/* MACRO.HEADER */
... [ macro code ] ...
/* MACRO.HEADER */
or
/* MACRO.HEADER */
... [ macro code ] ...
/* MACRO.END */
or
/* MACRO.HEADER
... [ macro code ] ...
*/
A macro block ends by default if a block of a different type begins:
/* MACRO.HEADER */
... [ header code ] ...
/* MACRO.BODY */
... [ body code ] ...
/* MACRO.EBD */
By default, each body is coupled with the header before. However, headers can be defined separately anywhere before the body (in the same file). Names come handy for this purpose:
/* MACRO.HEADER myMacro1 */
...
/* MACRO.HEADER myMacro1 */
/* MACRO.HEADER myMacro2 */
...
/* MACRO.HEADER myMacro2 */
// some code here
/* MACRO.BODY myMacro1 */
...
/* MACRO.BODY myMacro1 */
/* MACRO.BODY myMacro2 */
...
/* MACRO.BODY myMacro2 */
The header context defines "input" and "output" variables. Available input objects:
__dirname: the actual script path, regardless of where the utility is called fromconsole: the console object for console.log()require: the require() function for including modulespath: the path module for path operationsThe header must define two objects, the TOKENS function and the RANGES array.
The RANGES array is defined in the macro header and provides all the input data for the
TOKENS function. The data is provided in series which will be combined (all possible values)
when calling the TOKENS function. Keywords can be used to define the series:
{ each: <object> }: each value in an object, produces an array of all values{ keys: <object> }: all keys in an object, produces an array of all keys (strings or numbers){ values: <array> }: explicit list of values, copies array (eg [ 0, 5, 10 ]){ from: <number>, to: <number> }: all integers between from and to, inclusiveFor example, the following will produce a dataset of all possible pairs of months and years (between 2010 and 2020):
const RANGES = [
{ values: [ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" ] },
{ from: 2010, to: 2020 }
];
// resolves to
// [ [ "jan", 2010 ], [ "jan", 2011 ], ... , [ "dec", 2019 ], [ "dec", 2020 ] ]
If you want the list sorted by year, place the year { from ... to } on the first position in the
RANGES array.
const RANGES = [
{ from: 2010, to: 2020 },
{ values: [ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" ] }
];
// resolves to
// [ [ 2010, "jan" ], [ 2010, "feb" ], [ 2010, "mar" ], ... , [ 2020, "dec" ] ]
The TOKENS function is called with every set in the resolved ranges array. It has to be defined with as
many arguments as there are series in the RANGES array. The return value is an array of objects, with
each object containing token values. The objects returned in the array will be put together in the final
array used for expanding the macro body. You can return more objects or you can return no objects
(empty array) if you want to skip a data set altogether.
const TOKENS = (year, month) => {
// called automatically with month and year, eg: 'jan', 2010
// skip the month of july 2015
if (month === "jul" && year === 2015)
return [];
// for all other months, return the month and year as tokens
let ret = [ {
MONTH: month,
YEAR: year
} ];
// for the month of december, return an extra element for the end of year report
if (month === "dec")
ret.push({
MONTH: "final",
YEAR: year
});
return ret;
};
The function above will return all months and years as tokens, minus july 2015, and an extra element for each year with the month set to "final".
The macro body will be expanded, ie duplicated for each token set returned by the TOKENS function.
/* MACRO.BODY */
addEntry("%YEAR%", "%MONTH%");
/* MACRO.BODY */
The macro above will be expanded to:
addEntry("2010", "jan");
addEntry("2010", "feb");
...
addEntry("2010", "dec");
addEntry("2010", "final");
addEntry("2011", "jan");
addEntry("2011", "feb");
...
addEntry("2020", "dec");
addEntry("2020", "final");
Importing the module via require gives access to the two relevant functions, extractMacros and
processMacros.
extractMacros takes a single argument, the array of rows of code as they are read from the file,
and returns an object containing the list of extracted macros, as well as an array of strings containing
the remaining code and placeholders (processed macros will be inserted instead of placeholders):
macros: array of objects, the list of macros found in code, each described by:
name: string, name if anybody: array of strings, macro body code (this will be duplicated with tokens replaced)code: array of strings, the remaining rows of code; header bodies will be replaced by
a token such as <<< MACRO BODY [i] >>> where i is the actual index in the macros arrayThis object is used to call the processMacros function, which takes three arguments:
macros: array of objects, extracted macro objects
-header: array of strings, macro header code (will run in a vm)
body: array of strings, code template rows (with tokens to be replaced)code: array of strings, the code after macro extraction (with placeholders for macro output)scriptPath: string, the path to the code being processed, this will be used as __dirname in macro executionAnd returns an array of strings, the newly generated code with the macros expanded.
See the example above and documentation for more details.
FAQs
Macro Code Generator for nodejs
We found that macronator demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins 10 Minutes or Less, a podcast by Ali Rohde, to discuss the recent surge in open source supply chain attacks.

Research
/Security News
Campaign of 108 extensions harvests identities, steals sessions, and adds backdoors to browsers, all tied to the same C2 infrastructure.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.