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 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
- `npm i prettify-bru`
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
- Boom, now you're ready you can run:
39
+ ⚠️ This will modify the files in place, use with caution.
8
40
 
9
- `npx prettify-bru -d .`
41
+ To just do a single subdirectory, provide the path as an argument, so search the folder names "speed-tests":
10
42
 
11
- The above command will sweep through all directories looking to `.bru` files and it will find any `body:json` blocks and prettify the JSON inside.
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 { hideBin } from 'yargs/helpers';
5
- import { go } from './index.js';
4
+ import {hideBin} from 'yargs/helpers';
5
+ import {main} from './lib/main.js';
6
6
 
7
7
  const argv = yargs(hideBin(process.argv))
8
- .usage(`
9
- Usage: $0 [--write|-w] path
10
-
11
- Running the command with no arguments will modify all files
12
-
13
- `).options({
14
- w: {
15
- type: 'boolean',
16
- default: false
17
- },
18
- path: {
19
- default: '.',
20
- },
21
- })
22
- .describe({
23
- w: 'Write mode (Formats files in place, overwriting contents)',
24
- h: 'Display the help message',
25
- })
26
- .boolean(['w', 'h'])
27
- .help()
28
- .alias('h', 'help')
29
- .alias('w', 'write')
30
- .parse();
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
- yargs.showHelp();
33
+ yargs.showHelp();
34
34
  } else {
35
- go(argv.w, argv._[0]);
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.9",
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 (http://martinjoiner.co.uk)",
23
+ "author": "Martin Joiner (https://martinjoiner.co.uk)",
24
24
  "type": "module",
25
25
  "files": [
26
26
  "index.js",
27
27
  "cli.js",
28
- "src/"
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
@@ -1,3 +0,0 @@
1
- export function check() {
2
- console.log("This is check");
3
- }