prettify-bru 1.3.0 → 1.4.0

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/cli.js CHANGED
@@ -1,24 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import yargs from 'yargs';
4
- import {hideBin} from 'yargs/helpers';
5
- import {main} from './lib/main.mjs';
3
+ import yargs from 'yargs'
4
+ import {hideBin} from 'yargs/helpers'
5
+ import {main} from './lib/main.mjs'
6
6
 
7
7
  const argv = yargs(hideBin(process.argv))
8
- .command('$0 [path] [-w|--write] [--only "..."]', `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
- })
8
+ .command(
9
+ '$0 [path] [-w|--write] [--only "..."]',
10
+ `Formats all .bru files (including subdirectories)`,
11
+ yargs => {
12
+ return yargs.positional('path', {
13
+ describe: 'The root path to search from',
14
+ type: 'string',
15
+ demandOption: false,
16
+ default: '',
17
+ defaultDescription: 'Current working directory',
18
+ })
19
+ }
20
+ )
17
21
  .options({
18
22
  only: {
19
23
  describe: 'Limit to only 1 block type',
20
24
  type: 'string',
21
- choices: ['body:json', 'json', 'script:pre-request', 'pre-request', 'script:post-request', 'post-request', 'tests']
25
+ choices: [
26
+ 'body:json',
27
+ 'json',
28
+ 'script:pre-request',
29
+ 'pre-request',
30
+ 'script:post-request',
31
+ 'post-request',
32
+ 'tests',
33
+ ],
22
34
  },
23
35
  w: {
24
36
  describe: 'Write mode (Formats files in place, overwriting contents)',
@@ -29,12 +41,12 @@ const argv = yargs(hideBin(process.argv))
29
41
  })
30
42
  .boolean(['w', 'h'])
31
43
  .alias('h', 'help')
32
- .parse();
44
+ .parse()
33
45
 
34
46
  if (argv.h) {
35
- yargs.showHelp();
47
+ yargs.showHelp()
36
48
  } else {
37
- go(argv.path, argv.w, argv.only ?? null);
49
+ go(argv.path, argv.w, argv.only ?? null)
38
50
  }
39
51
 
40
52
  /**
@@ -44,13 +56,13 @@ if (argv.h) {
44
56
  */
45
57
  function go(path, write, only) {
46
58
  main(console, process.cwd(), path, write, only)
47
- .then(changesRequired => {
48
- if (changesRequired) {
59
+ .then(changesRequired => {
60
+ if (changesRequired) {
61
+ process.exitCode = 1
62
+ }
63
+ })
64
+ .catch(err => {
65
+ console.error(err)
49
66
  process.exitCode = 1
50
- }
51
- })
52
- .catch((err) => {
53
- console.error(err);
54
- process.exitCode = 1;
55
- });
67
+ })
56
68
  }
package/lib/files.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import fs from 'fs';
1
+ import fs from 'fs'
2
2
  import path from 'path'
3
3
 
4
4
  /**
@@ -6,7 +6,7 @@ import path from 'path'
6
6
  * @returns {string}
7
7
  */
8
8
  export function readFile(filePath) {
9
- return fs.readFileSync(filePath, "utf8")
9
+ return fs.readFileSync(filePath, 'utf8')
10
10
  }
11
11
 
12
12
  /**
@@ -15,20 +15,20 @@ export function readFile(filePath) {
15
15
  * @returns void
16
16
  */
17
17
  export function writeFile(filePath, contents) {
18
- fs.writeFileSync(filePath, contents, "utf8")
18
+ fs.writeFileSync(filePath, contents, 'utf8')
19
19
  }
20
20
 
21
21
  export function findFiles(path) {
22
- const files = [];
22
+ const files = []
23
23
 
24
24
  if (path.match(/\.bru$/) && fs.existsSync(path)) {
25
25
  return [path]
26
26
  }
27
27
 
28
- walkDir(path, (p) => {
29
- if (p.endsWith(".bru")) files.push(p);
30
- });
31
- return files;
28
+ walkDir(path, p => {
29
+ if (p.endsWith('.bru')) files.push(p)
30
+ })
31
+ return files
32
32
  }
33
33
 
34
34
  /**
@@ -42,20 +42,20 @@ export function findFiles(path) {
42
42
  */
43
43
  function walkDir(dir, onFile) {
44
44
  // Skip node_modules by default
45
- const skip = new Set(["node_modules", ".git"]);
46
- let entries;
45
+ const skip = new Set(['node_modules', '.git'])
46
+ let entries
47
47
  try {
48
- entries = fs.readdirSync(dir, {withFileTypes: true});
48
+ entries = fs.readdirSync(dir, {withFileTypes: true})
49
49
  } catch (e) {
50
- return;
50
+ return
51
51
  }
52
52
  for (const entry of entries) {
53
- const full = path.join(dir, entry.name);
53
+ const full = path.join(dir, entry.name)
54
54
  if (entry.isDirectory()) {
55
- if (skip.has(entry.name)) continue;
56
- walkDir(full, onFile);
55
+ if (skip.has(entry.name)) continue
56
+ walkDir(full, onFile)
57
57
  } else if (entry.isFile()) {
58
- onFile(full);
58
+ onFile(full)
59
59
  }
60
60
  }
61
61
  }
package/lib/format.mjs CHANGED
@@ -7,8 +7,8 @@ const prettierConfig = {
7
7
  singleQuote: false,
8
8
  useTabs: false,
9
9
  bracketSpacing: false,
10
- trailingComma: "none",
11
- endOfLine: "lf",
10
+ trailingComma: 'none',
11
+ endOfLine: 'lf',
12
12
  }
13
13
 
14
14
  const formattableBlocks = [
@@ -19,36 +19,41 @@ const formattableBlocks = [
19
19
  ]
20
20
 
21
21
  /**
22
- * Uses Prettier to format blocks of JSON/JavaScript
22
+ * Tidies overall structure of bru lang file and uses Prettier to format blocks of JSON/JavaScript
23
23
  *
24
24
  * @param {string} originalContents The file contents as loaded from file system
25
25
  * @param {?string} only Limit to only the block type with a name containing value
26
- * @returns {Promise<{newContents: string, blocksSearchedFor: number, changeable: number, error_messages: string[]}>}
26
+ * @returns {Promise<{newContents: string, blocksSearchedFor: number, changeable: boolean, errorMessages: string[]}>}
27
27
  */
28
- export async function formatBlocks(originalContents, only = null) {
29
-
28
+ export async function format(originalContents, only = null) {
30
29
  let fileOutcome = {
31
- newContents: originalContents.replace(/\r\n/g, "\n"),
30
+ newContents: originalContents.replace(/\r\n/g, '\n'),
32
31
  blocksSearchedFor: 0,
33
- changeable: 0,
34
- error_messages: []
32
+ changeable: false,
33
+ errorMessages: [],
35
34
  }
36
35
 
37
36
  let i
38
37
  for (i in formattableBlocks) {
39
38
  let [blockName, parser] = formattableBlocks[i]
40
- if (only !== null && !blockName.includes(only)) continue;
39
+ if (only !== null && !blockName.includes(only)) continue
41
40
 
42
41
  const blockOutcome = await formatBlock(fileOutcome.newContents, blockName, parser)
43
42
  fileOutcome.blocksSearchedFor++
44
- if (blockOutcome.error_message !== null) {
45
- fileOutcome.error_messages.push(blockOutcome.error_message)
43
+ if (blockOutcome.errorMessage !== null) {
44
+ fileOutcome.errorMessages.push(blockOutcome.errorMessage)
46
45
  } else if (blockOutcome.changeable) {
47
- fileOutcome.changeable++
46
+ fileOutcome.changeable = true
48
47
  fileOutcome.newContents = blockOutcome.fileContents
49
48
  }
50
49
  }
51
50
 
51
+ const overallOutcome = formatOverallStructure(fileOutcome.newContents)
52
+ if (overallOutcome.changeable) {
53
+ fileOutcome.changeable = true
54
+ fileOutcome.newContents = overallOutcome.fileContents
55
+ }
56
+
52
57
  return fileOutcome
53
58
  }
54
59
 
@@ -56,11 +61,10 @@ export async function formatBlocks(originalContents, only = null) {
56
61
  * @param {string} fileContents
57
62
  * @param {string} blockName
58
63
  * @param {string} parser
59
- * @returns {Promise<{fileContents: string, changeable: boolean, error_message: ?string}>}
64
+ * @returns {Promise<{fileContents: string, changeable: boolean, errorMessage: ?string}>}
60
65
  */
61
66
  async function formatBlock(fileContents, blockName, parser) {
62
-
63
- let outcome = {fileContents, changeable: false, error_message: null}
67
+ let outcome = {fileContents, changeable: false, errorMessage: null}
64
68
 
65
69
  const blockBodyRegex = new RegExp('\n' + blockName + ' [{]\\n(.+?)\\n}\\n', 's')
66
70
  const match = fileContents.match(blockBodyRegex)
@@ -69,28 +73,31 @@ async function formatBlock(fileContents, blockName, parser) {
69
73
  }
70
74
  const rawBody = match[1]
71
75
 
72
- // Remove 2-spaces of indentation, added due to being inside a Bru lang Multimap
73
- const unindented = rawBody.replace(/^ /gm, "")
76
+ // Remove 2-spaces of indentation, added due to being inside a Bru lang block
77
+ const unindented = rawBody.replace(/^ /gm, '')
74
78
 
75
- let prettierFormatted;
79
+ let prettierFormatted
76
80
 
77
81
  try {
78
- prettierFormatted = await prettier.format(unindented, Object.assign(prettierConfig, {parser}));
82
+ prettierFormatted = await prettier.format(
83
+ unindented,
84
+ Object.assign(prettierConfig, {parser})
85
+ )
79
86
  } catch (e) {
80
- outcome.error_message = `Prettier could not format ${blockName} because...\n${e.message}`;
87
+ outcome.errorMessage = `Prettier could not format ${blockName} because...\n${e.message}`
81
88
  return outcome
82
89
  }
83
90
 
84
- const bodyLines = prettierFormatted.split("\n");
91
+ const bodyLines = prettierFormatted.split('\n')
85
92
 
86
93
  // Remove leading/trailing empty lines
87
- while (bodyLines.length && bodyLines[0].trim() === "") bodyLines.shift();
88
- while (bodyLines.length && bodyLines[bodyLines.length - 1].trim() === "") bodyLines.pop();
94
+ while (bodyLines.length && bodyLines[0].trim() === '') bodyLines.shift()
95
+ while (bodyLines.length && bodyLines[bodyLines.length - 1].trim() === '') bodyLines.pop()
89
96
 
90
97
  // Indent the whole body by 2 spaces so it sits inside the Bru lang Multimap
91
- const indentedLines = bodyLines.map((l) => " " + l)
98
+ const indentedLines = bodyLines.map(l => ' ' + l)
92
99
 
93
- const formattedBody = indentedLines.join("\n");
100
+ const formattedBody = indentedLines.join('\n')
94
101
 
95
102
  if (formattedBody === rawBody) {
96
103
  // Nothing has changed after formatting, so this block is not changeable
@@ -99,5 +106,20 @@ async function formatBlock(fileContents, blockName, parser) {
99
106
 
100
107
  outcome.fileContents = fileContents.replace(rawBody, formattedBody)
101
108
  outcome.changeable = true
102
- return outcome;
109
+ return outcome
110
+ }
111
+
112
+ /**
113
+ * @param {string} fileContents
114
+ * @returns {fileContents: string, changeable: boolean}
115
+ */
116
+ function formatOverallStructure(fileContents) {
117
+ if (fileContents.match(/\n[}]\n[\s]+\n\w+/s) === null) {
118
+ return {fileContents, changeable: false}
119
+ }
120
+
121
+ return {
122
+ fileContents: fileContents.replaceAll(/(\n[}]\n)([\s]+)(\n\w+)/gs, '$1$3'),
123
+ changeable: true,
124
+ }
103
125
  }
package/lib/main.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import {findFiles, readFile, writeFile} from './files.mjs';
2
- import {formatBlocks} from './format.mjs'
1
+ import {findFiles, readFile, writeFile} from './files.mjs'
2
+ import {format} from './format.mjs'
3
3
 
4
4
  /**
5
5
  * Finds all .bru files and formats contents
@@ -12,7 +12,6 @@ import {formatBlocks} from './format.mjs'
12
12
  * @returns {Promise<boolean>} True means some files contained errors or needed reformatting
13
13
  */
14
14
  export async function main(console, cwd, path, write, only = null) {
15
-
16
15
  if (path === '') {
17
16
  path = cwd
18
17
  } else {
@@ -20,56 +19,60 @@ export async function main(console, cwd, path, write, only = null) {
20
19
  path = cwd + '/' + path
21
20
  }
22
21
 
23
- const files = findFiles(path);
22
+ const files = findFiles(path)
24
23
 
25
24
  if (files.length === 0) {
26
- console.log("No .bru files found.");
27
- return;
25
+ console.log('No .bru files found.')
26
+ return
28
27
  }
29
28
 
30
- console.log(`Found ${files.length} .bru file(s)\n`);
29
+ console.log(`Found ${files.length} .bru file(s)\n`)
31
30
 
32
- let changeableFiles = [];
33
- let erroredFiles = [];
31
+ let changeableFiles = []
32
+ let erroredFiles = []
34
33
 
35
34
  for (const filePath of files) {
36
- const outcome = await processFile(filePath, write, only);
35
+ const outcome = await processFile(filePath, write, only)
37
36
 
38
- let displayFilePath = filePath.replace(new RegExp('^' + cwd + '/'), "");
37
+ let displayFilePath = filePath.replace(new RegExp('^' + cwd + '/'), '')
39
38
 
40
39
  if (outcome.changeable) {
41
- changeableFiles.push({displayFilePath, outcome});
42
- } else if (outcome.error_messages.length) {
43
- erroredFiles.push({displayFilePath, outcome});
40
+ changeableFiles.push({displayFilePath, outcome})
41
+ } else if (outcome.errorMessages.length) {
42
+ erroredFiles.push({displayFilePath, outcome})
44
43
  }
45
44
  }
46
45
 
47
- const changeablePrefix = write ? 'Reformatted blocks' : 'Found blocks that need reformatting';
46
+ const changeablePrefix = write ? 'Reformatted lines' : 'Found lines that need reformatting'
48
47
 
49
48
  if (changeableFiles.length) {
50
- const emoji = write ? '✏️' : '🔍';
51
- console.log(`\x1b[4m\x1b[32m${changeablePrefix} in ${changeableFiles.length} file(s):\x1b[0m\n`);
52
- changeableFiles.forEach((r) => console.log(`${emoji} \x1b[32m${r.displayFilePath}\x1b[0m`));
53
- console.log(" ")
49
+ const emoji = write ? '✏️' : '🔍'
50
+ console.log(
51
+ `\x1b[4m\x1b[32m${changeablePrefix} in ${changeableFiles.length} file(s):\x1b[0m\n`
52
+ )
53
+ changeableFiles.forEach(r => console.log(`${emoji} \x1b[32m${r.displayFilePath}\x1b[0m`))
54
+ console.log(' ')
54
55
  }
55
56
 
56
57
  if (erroredFiles.length) {
57
- console.warn(`\x1b[4m\x1b[33mEncountered errors in ${erroredFiles.length} file(s):\x1b[0m\n`);
58
+ console.warn(
59
+ `\x1b[4m\x1b[33mEncountered errors in ${erroredFiles.length} file(s):\x1b[0m\n`
60
+ )
58
61
  erroredFiles.forEach((r, i) => {
59
62
  console.warn(`${i + 1}) ${r.displayFilePath}\n`)
60
- r.outcome.error_messages.forEach((err) => {
63
+ r.outcome.errorMessages.forEach(err => {
61
64
  console.warn(`⚠️ \x1b[33m${err}\x1b[0m\n`)
62
65
  })
63
66
  })
64
67
  }
65
68
 
66
- const requireNothing = files.length - changeableFiles.length - erroredFiles.length;
69
+ const requireNothing = files.length - changeableFiles.length - erroredFiles.length
67
70
  console.log(
68
- `\x1b[35mProcessed ${files.length} .bru file(s):\x1b[0m ${changeablePrefix} in ${changeableFiles.length}. `
69
- + `Encountered errors in ${erroredFiles.length}. ${requireNothing} file(s) did not require any changes.`
70
- );
71
+ `\x1b[35mProcessed ${files.length} .bru file(s):\x1b[0m ${changeablePrefix} in ${changeableFiles.length}. ` +
72
+ `Encountered errors in ${erroredFiles.length}. ${requireNothing} file(s) did not require any changes.`
73
+ )
71
74
 
72
- return (erroredFiles.length > 0 || changeableFiles.length > 0)
75
+ return erroredFiles.length > 0 || changeableFiles.length > 0
73
76
  }
74
77
 
75
78
  /**
@@ -79,13 +82,13 @@ export async function main(console, cwd, path, write, only = null) {
79
82
  * @returns {Promise<void>}
80
83
  */
81
84
  async function processFile(filePath, write, only) {
82
- const original = readFile(filePath);
85
+ const original = readFile(filePath)
83
86
 
84
- const fileOutcome = await formatBlocks(original, only);
87
+ const fileOutcome = await format(original, only)
85
88
 
86
- if (write && fileOutcome.changeable > 0) {
87
- writeFile(filePath, fileOutcome.newContents);
89
+ if (write && fileOutcome.changeable) {
90
+ writeFile(filePath, fileOutcome.newContents)
88
91
  }
89
92
 
90
- return fileOutcome;
93
+ return fileOutcome
91
94
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prettify-bru",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Prettifies JSON and JavaScript blocks in Bruno .bru files",
5
5
  "keywords": [
6
6
  "bruno",
@@ -32,7 +32,9 @@
32
32
  "yargs": "18.0.0"
33
33
  },
34
34
  "scripts": {
35
- "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --verbose"
35
+ "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --verbose",
36
+ "prettier:check": "npx prettier --check --parser babel '**/*.mjs' '**/*.js'",
37
+ "prettier:fix": "npx prettier --write --parser babel '**/*.mjs' '**/*.js'"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@jest/globals": "^30.2.0",