narrange v1.0.5
NArrange
NArrange is a .NET code beautifier that automatically organizes code members and elements within .NET classes.
NArrange executes a Windows .exe executable (as a child process) and thus only works on a Windows environment.
Install
Install as a (dev) package dependency:
npm install narrange -D
Quick start
From your node.js script, execute narrange.exe in a child process
const { createNArrange } = require("narrange");
const path = require("path");
const { narrange, ctx } = createNArrange({
srcPath: path.join(__dirname, "src/apps/mango"),
configFilePath: path.join(__dirname, "config/NArrange.xml")
// set debug mode
// debugOn: true
});
console.log(ctx);
narrange();Note that the factory method createNArrange returns both narrange and a ctx context object.
Look at the ctx object to ensure you have configured narrange correctly.
During experimentation, pass debugOn: true as an option to createNArrange
Pre-commit hooks
You can use this recipe to integrate NArrange with git hooks, such as pre-commit hooks:
- Setup git hooks via husky
- Create node script to trigger on hook
Setup hooks
Install and setup husky
Add the following to your package.json file (or create a new one using npm init)
{
"devDependencies": {
"narrange": "~1.0.0"
"husky": "~1.3.1"
},
"husky": {
"hooks": {
"pre-commit": "node format-csharp.js"
}
}
}Run npm install from your Terminal to install dependencies: husky and narrange
Create node script
Create a Node script format-csharp.js that runs narrange.exe on your source files using
a config file with your specific preferences.
Assuming we put the script in a /scripts folder in the project root
const { createNArrange } = require("narrange");
const path = require("path");
const rootPath = path.join(__dirname, "../");
const srcPath = path.join(rootPath, "Areas")
const configFilePath = path.join(rootPath, "config/NArrange.xml")
const { narrange } = createNArrange({
srcPath
configFilePath
});
narrange();Tabs configuration
In case you want to convert spaces to tabs, you can use one of the included configuration files
config/Tabs4.xmlconverts 4 spaces to 1 tabconfig/Tabs2.xmlconverts 2 spaces to 1 tab
Assuming we put the script in a /scripts folder in the project root
const path = require("path");
const rootPath = path.join(__dirname, "../");
const narrangeHomePath = path.join(rootPath, "node_modules/narrange");
const narrangeConfigPath = path.join(narrangeHomePath, "config");
// const narrangeLibPath = path.join(narrangeHomePath, "lib");
const configFilePath = path.join(narrangeConfigPath, "Tabs4.xml")sFormatting
To create your own formatting rules, by specifying your own rules in the <Formatting> section on the config file. Use any of the files included in config or lib folders of this package.
<Formatting>
<Tabs Style="Tabs" SpacesPerTab="2"/>
<ClosingComments Enabled="false" Format="End $(ElementType) $(Name)"/>
<Regions Style="NoDirective" />
<Usings MoveTo="Namespace"/>
<LineSpacing RemoveConsecutiveBlankLines="true" />
</Formatting>Note that you can use the narrange-config.exe to edit a configuration file using a GUI, which ensures you can only populate it with valid values.
Simple tabs formatting configuration
Simple configuration to force 2 spaces to be converted to a tab:
const { narrange } = createNArrange({
srcPath: path.join(__dirname, "src/apps/mango"),
tabs: 4 // use instead of configFilePath to select built in Tabs config file
});
narrange();createNArrange
The function createNArrange creates a child process which executes the narrange.exe executable.
createNArrange accepts the following options:
onErrorcustom stderr handler functiononOutcustom stdout handler functioncreateMainHandlerfactory method to create main handlercreateExitHandlerfactory method to create process exit handlercreateWritersfactory method to create writers (error, info, debug methods)createCommandfactory method to create the command to execute in the child processexitHandlerprocess exit handler function (takes precedence over factory)mainHandlermain handler function (takes precedence over factory)srcPathpath to input folder for narrange (location folder of source files to process)configFilePathpath to narrange config fileexePathpath to narrange exe to override version available in this moduletabsspaces count to tabs used in formatting (used to select built in Tabs config file) -2or4debugOnturn debug mode on or off (default: false)
Note that all these options are optional. If any option is left out, a default value is used.
Custom narrange setup example :
const { createNArrange } = require("narrange");
const path = require("path");
const createMainHandler = (opts = {}) => {
return (err, stdout, stderr) => {
// ..
if (err) {
opts.onError(err);
}
};
};
const onError = (err) {
console.error(err);
}
const { narrange } = createNArrange({
srcPath: path.join(__dirname, "src/apps/mango"),
configFilePath: path.join(__dirname, "config/NArrange.xml"),
createMainHandler,
onOut,
onError,
// tabs: 2 // use instead of configFilePath to select built in Tabs config file
});
narrange();Exports
narrange exports the following "building blocks":
exitHandlerfunctionmainHandlerfunctioncreateMainHandlerfactory functioncreateExitHandlerfactory functioncreateWritersfunction to create info, error, and debug functionsdefaultsconfig objectpathsconfig object
This should make it easy to build a custom narrange solution to suit your needs.
Full script example
The following example uses minimist to parse process args passed from shell.
const path = require("path");
const minimist = require('minimist');
const { createNArrange } = require("narrange");
const processArgs = process.argv.slice(2);
const opts = {
alias: {
h: 'help',
s: 'src',
t: 'tabs'
}
};
// args is an object, with key for each named argument
const args = minimist(processArgs, opts);
const defaults = {
srcFolder: "./",
tabs: 4
}
if (args.help) {
console.log(`
format-cs
---------
-s src folder (default: ./ )
-t spaces per tab (default: 4)
`)
process.exit(0);
}
const srcFolder = args.src || defaults.srcFolders;
const tabs = args.tabs || defaults.tabs;
const rootPath = path.join(__dirname, "..");
const srcPath = path.join(rootPath, srcFolder),
createNArrange({
srcPath,
tabs
});Tests
You can run some basic tests using jest
$ npm testNote that the executable narrange.exe can only be executed on a Windows (perhaps also using Mono?) platform.
More resources
Recipes
gits: PowerShell - Recurse Project
Format-Document PowerShell function which leverages Recurse-Project and automates calling into Visual Studio’s Format Document command.
function Format-Document {
param(
[parameter(ValueFromPipelineByPropertyName = $true)]
[string[]]$ProjectName
)
Process {
$ProjectName | %{
Recurse-Project -ProjectName $_ -Action
{
param($item)
if($item.Type -eq 'Folder' -or !$item.Language)
{
return
}
$win = $item.ProjectItem.Open('{7651A701-06E5-11D1-8EBD-00A0C90F26EA}')
if ($win)
{
Write-Host "Processing `"$($item.ProjectItem.Name)`"" [System.Threading.Thread]::Sleep(100) $win.Activate() $item.ProjectItem.Document.DTE.ExecuteCommand('Edit.FormatDocument') $item.ProjectItem.Document.DTE.ExecuteCommand('Edit.RemoveAndSort') $win.Close(1)
}
}
}
}
}Format-Document can be configured for Visual Studio and used to run your existing Format Document command on a subset of project files, using recurse project to iterate and select the files to be processed.
Extensions
- VS extension: FormatAllFiles
- VS extension: NArrangeVS
- Code formatter (based on Roslyn)
More docs
See ./Format-CSharp.md for more details on using narrange directly.
License
See NArrange license terms