prettify-bru 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -5
- package/cli.js +34 -27
- package/lib/format.js +91 -0
- package/lib/main.js +107 -0
- package/package.json +4 -3
- package/index.js +0 -13
- package/src/index.js +0 -3
package/README.md
CHANGED
|
@@ -1,11 +1,45 @@
|
|
|
1
|
-
# Prettify Bru Files
|
|
1
|
+
# Prettify Bruno Bru Files
|
|
2
|
+
|
|
3
|
+
A simple CLI tool to prettify the contents of `.bru` files.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Requires Node.js 20+
|
|
2
8
|
|
|
3
9
|
To install in your project, run:
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
```
|
|
12
|
+
npm i prettify-bru
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Boom, now you should be ready to go!
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
Get a non-destructive report of all files that could potentially be re-formatted by running:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
npx prettify-bru
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The above command will walk all subdirectories finding `.bru` files.
|
|
26
|
+
With each file it will assess the formatting of the JSON/JavaScript inside the following types of block:
|
|
27
|
+
|
|
28
|
+
- `body:json` will be parsed with the JSON parser
|
|
29
|
+
- `script:pre-request` blocks parsed with Babel
|
|
30
|
+
- `script:post-request` blocks parsed with Babel
|
|
31
|
+
- `tests` blocks parsed with Babel
|
|
32
|
+
|
|
33
|
+
To actually **modify** the files, I recommend committing your changes before doing this, run the command with the `--write` flag:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
npx prettify-bru --write
|
|
37
|
+
```
|
|
6
38
|
|
|
7
|
-
|
|
39
|
+
⚠️ This will modify the files in place, use with caution.
|
|
8
40
|
|
|
9
|
-
|
|
41
|
+
To just do a single subdirectory, provide the path as an argument, so search the folder names "speed-tests":
|
|
10
42
|
|
|
11
|
-
|
|
43
|
+
```
|
|
44
|
+
npx prettify-bru speed-tests
|
|
45
|
+
```
|
package/cli.js
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import yargs from 'yargs';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import {hideBin} from 'yargs/helpers';
|
|
5
|
+
import {main} from './lib/main.js';
|
|
6
6
|
|
|
7
7
|
const argv = yargs(hideBin(process.argv))
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
8
|
+
.command('$0 [path] [-w|--write]', `Formats all .bru files (including subdirectories)`, (yargs) => {
|
|
9
|
+
return yargs.positional('path', {
|
|
10
|
+
describe: 'The root path to search from',
|
|
11
|
+
type: 'string',
|
|
12
|
+
demandOption: false,
|
|
13
|
+
default: '',
|
|
14
|
+
defaultDescription: 'Current working directory'
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
.options({
|
|
18
|
+
w: {
|
|
19
|
+
alias: 'write',
|
|
20
|
+
type: 'boolean',
|
|
21
|
+
default: false,
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
.describe({
|
|
25
|
+
w: 'Write mode (Formats files in place, overwriting contents)',
|
|
26
|
+
h: 'Display the help message',
|
|
27
|
+
})
|
|
28
|
+
.boolean(['w', 'h'])
|
|
29
|
+
.alias('h', 'help')
|
|
30
|
+
.parse();
|
|
31
31
|
|
|
32
32
|
if (argv.h) {
|
|
33
|
-
|
|
33
|
+
yargs.showHelp();
|
|
34
34
|
} else {
|
|
35
|
-
|
|
35
|
+
go(argv.path, argv.w);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function go(path, write) {
|
|
39
|
+
main(process.cwd(), path, write).catch((err) => {
|
|
40
|
+
console.error(err);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
});
|
|
36
43
|
}
|
package/lib/format.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uses Prettier to format blocks of JSON/JavaScript
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import prettier from 'prettier'
|
|
6
|
+
|
|
7
|
+
// This Prettier config should match what the Bruno GUI implements
|
|
8
|
+
const prettierConfig = {
|
|
9
|
+
tabWidth: 4,
|
|
10
|
+
useTabs: false,
|
|
11
|
+
bracketSpacing: false,
|
|
12
|
+
trailingComma: "none",
|
|
13
|
+
endOfLine: "endOfLine",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const formattableBlocks = [
|
|
17
|
+
['body:json', 'json'],
|
|
18
|
+
['script:pre-request', 'babel'],
|
|
19
|
+
['script:post-request', 'babel'],
|
|
20
|
+
['tests', 'babel'],
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export async function formatBlocks(originalContents) {
|
|
25
|
+
|
|
26
|
+
let fileOutcome = {
|
|
27
|
+
newContents: originalContents.replace(/\r\n/g, "\n"),
|
|
28
|
+
changeable: false,
|
|
29
|
+
error_messages: []
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let i
|
|
33
|
+
for (i in formattableBlocks) {
|
|
34
|
+
const blockOutcome = await formatBlock(fileOutcome.newContents, ...formattableBlocks[i])
|
|
35
|
+
if (blockOutcome.error_message !== null) {
|
|
36
|
+
fileOutcome.error_messages.push(blockOutcome.error_message)
|
|
37
|
+
} else if (blockOutcome.changeable) {
|
|
38
|
+
fileOutcome.changeable = true
|
|
39
|
+
fileOutcome.newContents = blockOutcome.fileContents;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return fileOutcome
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function formatBlock(fileContents, blockName, parser) {
|
|
47
|
+
|
|
48
|
+
let outcome = {fileContents, changeable: false, error_message: null};
|
|
49
|
+
|
|
50
|
+
const blockBodyRegex = new RegExp('\n' + blockName + ' [{]\\n(.+?)\\n}\\n', 's')
|
|
51
|
+
const match = fileContents.match(blockBodyRegex)
|
|
52
|
+
if (match === null) {
|
|
53
|
+
return outcome
|
|
54
|
+
}
|
|
55
|
+
const rawBody = match[1]
|
|
56
|
+
|
|
57
|
+
// Remove 2-spaces of indentation, added due to being inside a Bru lang Multimap
|
|
58
|
+
const unindented = rawBody.replace(/^ /gm, "")
|
|
59
|
+
|
|
60
|
+
let prettierFormatted;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
prettierFormatted = await prettier.format(unindented, {
|
|
64
|
+
prettierConfig,
|
|
65
|
+
parser,
|
|
66
|
+
}).then((formatted) => formatted);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
outcome.error_message = `Prettier could not format ${blockName} because...\n${e.message}`;
|
|
69
|
+
return outcome
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const bodyLines = prettierFormatted.split("\n");
|
|
73
|
+
|
|
74
|
+
// Remove leading/trailing empty lines
|
|
75
|
+
while (bodyLines.length && bodyLines[0].trim() === "") bodyLines.shift();
|
|
76
|
+
while (bodyLines.length && bodyLines[bodyLines.length - 1].trim() === "") bodyLines.pop();
|
|
77
|
+
|
|
78
|
+
// Indent the whole body by 2 spaces so it sits inside the Bru lang Multimap
|
|
79
|
+
const indentedLines = bodyLines.map((l) => " " + l)
|
|
80
|
+
|
|
81
|
+
const formattedBody = indentedLines.join("\n");
|
|
82
|
+
|
|
83
|
+
if (formattedBody === rawBody) {
|
|
84
|
+
// Nothing has changed after formatting, so this block is not changeable
|
|
85
|
+
return outcome
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
outcome.fileContents = fileContents.replace(rawBody, formattedBody)
|
|
89
|
+
outcome.changeable = true
|
|
90
|
+
return outcome;
|
|
91
|
+
}
|
package/lib/main.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds all .bru files and formats contents
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
import {formatBlocks} from './format.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param cwd {String} Current working directory
|
|
12
|
+
* @param path {String}
|
|
13
|
+
* @param write {Boolean}
|
|
14
|
+
* @returns {Promise<void>}
|
|
15
|
+
*/
|
|
16
|
+
export async function main(cwd, path, write) {
|
|
17
|
+
|
|
18
|
+
if (path === '') {
|
|
19
|
+
path = cwd
|
|
20
|
+
} else {
|
|
21
|
+
// Append the relative path to the current working directory
|
|
22
|
+
path = cwd + '/' + path
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const files = [];
|
|
26
|
+
|
|
27
|
+
walkDir(path, (p) => {
|
|
28
|
+
if (p.endsWith(".bru")) files.push(p);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (files.length === 0) {
|
|
32
|
+
console.log("No .bru files found.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(`Found ${files.length} .bru file(s)\n`);
|
|
37
|
+
|
|
38
|
+
let changeableFiles = [];
|
|
39
|
+
let erroredFiles = [];
|
|
40
|
+
|
|
41
|
+
for (const filePath of files) {
|
|
42
|
+
const outcome = await processFile(filePath, write);
|
|
43
|
+
|
|
44
|
+
let displayFilePath = filePath.replace(new RegExp('^' + cwd), "");
|
|
45
|
+
|
|
46
|
+
if (outcome.changed) {
|
|
47
|
+
changeableFiles.push({displayFilePath, outcome});
|
|
48
|
+
} else if (outcome.error_messages.length) {
|
|
49
|
+
erroredFiles.push({displayFilePath, outcome});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const changeablePrefix = write ? 'Reformatted blocks' : 'Found blocks that need reformatting';
|
|
54
|
+
|
|
55
|
+
if (changeableFiles.length) {
|
|
56
|
+
const emoji = write ? '✏️' : '🔍';
|
|
57
|
+
console.log(`\x1b[4m\x1b[32m${changeablePrefix} in ${changeableFiles.length} file(s):\x1b[0m\n`);
|
|
58
|
+
changeableFiles.forEach((r) => console.log(`${emoji} \x1b[32m${r.displayFilePath}\x1b[0m`));
|
|
59
|
+
console.log(" ")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (erroredFiles.length) {
|
|
63
|
+
console.warn(`\x1b[4m\x1b[33mEncountered errors in ${erroredFiles.length} file(s):\x1b[0m\n`);
|
|
64
|
+
erroredFiles.forEach((r, i) => {
|
|
65
|
+
console.warn(`${i + 1}) ${r.displayFilePath}\n`)
|
|
66
|
+
r.outcome.error_messages.forEach((err) => {
|
|
67
|
+
console.warn(`⚠️ \x1b[33m${err}\x1b[0m\n`)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(
|
|
73
|
+
`\x1b[35mProcessed ${files.length} .bru file(s).\x1b[0m ${changeablePrefix} in ${changeableFiles.length}. Encountered errors in ${erroredFiles.length}.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function walkDir(dir, onFile) {
|
|
78
|
+
// Skip node_modules by default
|
|
79
|
+
const skip = new Set(["node_modules", ".git"]);
|
|
80
|
+
let entries;
|
|
81
|
+
try {
|
|
82
|
+
entries = fs.readdirSync(dir, {withFileTypes: true});
|
|
83
|
+
} catch (e) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const full = path.join(dir, entry.name);
|
|
88
|
+
if (entry.isDirectory()) {
|
|
89
|
+
if (skip.has(entry.name)) continue;
|
|
90
|
+
walkDir(full, onFile);
|
|
91
|
+
} else if (entry.isFile()) {
|
|
92
|
+
onFile(full);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function processFile(filePath, write) {
|
|
98
|
+
const original = fs.readFileSync(filePath, "utf8");
|
|
99
|
+
|
|
100
|
+
const fileOutcome = await formatBlocks(original);
|
|
101
|
+
|
|
102
|
+
if (write && fileOutcome.changed) {
|
|
103
|
+
fs.writeFileSync(filePath, fileOutcome.newContents, "utf8");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return fileOutcome;
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prettify-bru",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Prettifies JSON and JavaScript blocks in Bruno .bru files",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bruno",
|
|
@@ -20,15 +20,16 @@
|
|
|
20
20
|
"url": "git+https://github.com/martinjoiner/prettify-bru.git"
|
|
21
21
|
},
|
|
22
22
|
"license": "GPL-3.0-or-later",
|
|
23
|
-
"author": "Martin Joiner (
|
|
23
|
+
"author": "Martin Joiner (https://martinjoiner.co.uk)",
|
|
24
24
|
"type": "module",
|
|
25
25
|
"files": [
|
|
26
26
|
"index.js",
|
|
27
27
|
"cli.js",
|
|
28
|
-
"
|
|
28
|
+
"lib/"
|
|
29
29
|
],
|
|
30
30
|
"bin": "./cli.js",
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"prettier": "^3.3.3",
|
|
32
33
|
"yargs": "18.0.0"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
package/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function go(path, write) {
|
|
2
|
-
if (path === '.') {
|
|
3
|
-
path = process.cwd()
|
|
4
|
-
} else if (!/^\//.test(path)) {
|
|
5
|
-
// Path is absolute, do not modify
|
|
6
|
-
} else {
|
|
7
|
-
// Turn relative path into absolute
|
|
8
|
-
path = process.cwd() + '/' + path
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
console.log("go() method inside index.js");
|
|
12
|
-
console.log(path, write);
|
|
13
|
-
};
|
package/src/index.js
DELETED