prettify-bru 1.6.0 → 1.8.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/README.md +47 -4
- package/lib/config.mjs +40 -12
- package/lib/format.mjs +82 -45
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI tool to [prettify and format Bruno `.bru` files](https://www.npmjs.com/package/prettify-bru).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Removes junk and makes code shorter and more transferable between systems.
|
|
6
|
+
Imposes a standard format on all blocks of JSON and JavaScript code across multiple [Bruno](https://www.usebruno.com/) `.bru` files in your project.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
`body:json` blocks are formatted using [jsonc-parser](https://www.npmjs.com/package/jsonc-parser)
|
|
9
|
+
|
|
10
|
+
`script:pre-request`, `script:post-response` and `tests` blocks are formatted using [Prettier](https://prettier.io/) with [Babel](https://babeljs.io/docs/babel-parser) parser.
|
|
8
11
|
|
|
9
12
|
## Table of contents
|
|
10
13
|
|
|
@@ -117,7 +120,47 @@ npm prettify-bru --write --only body:json speed-tests/get-all.bru
|
|
|
117
120
|
|
|
118
121
|
## Config file
|
|
119
122
|
|
|
120
|
-
|
|
123
|
+
Create a `.prettifybrurc` file containing a JSON object with one or more of the following properties:
|
|
124
|
+
|
|
125
|
+
### Agnostic File Paths
|
|
126
|
+
|
|
127
|
+
Property: `agnosticFilePaths` {boolean} (Default: `true`)
|
|
128
|
+
|
|
129
|
+
Replaces backslash folder separators in filenames with forward slashes so they work on Windows, Mac and Linux
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
file: @file(\Images\Memes\3-Spidermen.jpg) @contentType(image/jpeg)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
will be changed to...
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
file: @file(/Images/Memes/3-Spidermen.jpg) @contentType(image/jpeg)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Shorten Getters
|
|
142
|
+
|
|
143
|
+
Property: `shortenGetters` {boolean} (Default: `true`)
|
|
144
|
+
|
|
145
|
+
Shorten code by replacing uses of getters with property references
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
expect(res.getStatus()).to.eql(200)
|
|
149
|
+
expect(res.getBody().name).to.eql("Dave")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The above will become...
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
expect(res.status).to.eql(200)
|
|
156
|
+
expect(res.body.name).to.eql("Dave")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Prettier
|
|
160
|
+
|
|
161
|
+
Property: `prettier` {Object} (Default: `{}`)
|
|
162
|
+
|
|
163
|
+
The `prettier` property object can contain overrides for any of the [Prettier options](https://prettier.io/docs/options).
|
|
121
164
|
|
|
122
165
|
For example, to increase the line length limit from the default 80 up to 120 characters, your file would contain:
|
|
123
166
|
|
|
@@ -129,7 +172,7 @@ For example, to increase the line length limit from the default 80 up to 120 cha
|
|
|
129
172
|
}
|
|
130
173
|
```
|
|
131
174
|
|
|
132
|
-
*Note: Config file is supported from version 1.6.0 and above.*
|
|
175
|
+
*Note: Config file is supported from version [1.6.0](CHANGELOG.md#160) and above.*
|
|
133
176
|
|
|
134
177
|
## Automatically checking PRs
|
|
135
178
|
|
package/lib/config.mjs
CHANGED
|
@@ -2,6 +2,20 @@ import {readIfExists} from './files.mjs'
|
|
|
2
2
|
|
|
3
3
|
const configFilename = '.prettifybrurc'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} PrettifyBruConfig
|
|
7
|
+
* @property {boolean} agnosticFilePaths
|
|
8
|
+
* @property {boolean} shortenGetters
|
|
9
|
+
* @property {Object} prettier Prettier options
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {PrettifyBruConfig} */
|
|
13
|
+
export const defaultConfig = {
|
|
14
|
+
agnosticFilePaths: true,
|
|
15
|
+
shortenGetters: true,
|
|
16
|
+
prettier: {},
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
/**
|
|
6
20
|
*
|
|
7
21
|
* @param {Object} console
|
|
@@ -44,21 +58,26 @@ export function parseFile(console, fileContents) {
|
|
|
44
58
|
|
|
45
59
|
let config = {}
|
|
46
60
|
|
|
47
|
-
const supportedProperties =
|
|
61
|
+
const supportedProperties = {
|
|
62
|
+
agnosticFilePaths: 'a boolean',
|
|
63
|
+
shortenGetters: 'a boolean',
|
|
64
|
+
prettier: 'an object',
|
|
65
|
+
}
|
|
48
66
|
Object.keys(fileConfig).forEach(key => {
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
if (Object.hasOwn(supportedProperties, key)) {
|
|
68
|
+
const value = fileConfig[key]
|
|
69
|
+
const validType = supportedProperties[key]
|
|
70
|
+
const validates = validators[validType]
|
|
71
|
+
|
|
72
|
+
if (validates(value)) {
|
|
73
|
+
config[key] = fileConfig[key]
|
|
74
|
+
} else {
|
|
75
|
+
console.warn(
|
|
76
|
+
`⚠️ \x1b[33m"${key}" is not correct type, it should be ${validType}\x1b[0m`
|
|
77
|
+
)
|
|
58
78
|
}
|
|
59
|
-
config[key] = fileConfig[key]
|
|
60
79
|
} else {
|
|
61
|
-
console.warn(`⚠️ \x1b[
|
|
80
|
+
console.warn(`⚠️ \x1b[33mIgnoring unsupported property "${key}"\x1b[0m`)
|
|
62
81
|
}
|
|
63
82
|
})
|
|
64
83
|
|
|
@@ -66,3 +85,12 @@ export function parseFile(console, fileContents) {
|
|
|
66
85
|
|
|
67
86
|
return config
|
|
68
87
|
}
|
|
88
|
+
|
|
89
|
+
const validators = {
|
|
90
|
+
'an object': value => {
|
|
91
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
92
|
+
},
|
|
93
|
+
'a boolean': value => {
|
|
94
|
+
return typeof value === 'boolean'
|
|
95
|
+
},
|
|
96
|
+
}
|
package/lib/format.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import prettier from 'prettier'
|
|
2
|
+
import {defaultConfig} from './config.mjs'
|
|
3
|
+
import {format as jsoncFormat, applyEdits} from 'jsonc-parser'
|
|
2
4
|
|
|
3
5
|
// This Prettier config should match what the Bruno GUI implements
|
|
4
6
|
const defaultPrettierConfig = {
|
|
@@ -10,14 +12,10 @@ const defaultPrettierConfig = {
|
|
|
10
12
|
printWidth: 80,
|
|
11
13
|
trailingComma: 'none',
|
|
12
14
|
endOfLine: 'lf',
|
|
15
|
+
parser: 'babel',
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
const formattableBlocks = [
|
|
16
|
-
['body:json', 'json'],
|
|
17
|
-
['script:pre-request', 'babel'],
|
|
18
|
-
['script:post-response', 'babel'],
|
|
19
|
-
['tests', 'babel'],
|
|
20
|
-
]
|
|
18
|
+
const formattableBlocks = ['body:json', 'script:pre-request', 'script:post-response', 'tests']
|
|
21
19
|
|
|
22
20
|
/**
|
|
23
21
|
* @typedef {Object} FileOutcome
|
|
@@ -28,14 +26,26 @@ const formattableBlocks = [
|
|
|
28
26
|
*/
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
|
-
* Tidies
|
|
29
|
+
* Tidies structure of bru lang file, makes code shorter and operating system agnostic,
|
|
30
|
+
* applies standard formatting to both JSON and JavaScript blocks
|
|
32
31
|
*
|
|
33
32
|
* @param {string} originalContents The file contents as loaded from file system
|
|
34
33
|
* @param {?string} only Limit to only the block type with a name containing value
|
|
35
|
-
* @param {Object}
|
|
34
|
+
* @param {Object} configOverrides Could be whole PrettifyBruConfig or partial
|
|
36
35
|
* @returns {Promise<FileOutcome>}
|
|
37
36
|
*/
|
|
38
|
-
export async function format(originalContents, only = null,
|
|
37
|
+
export async function format(originalContents, only = null, configOverrides = {}) {
|
|
38
|
+
/** @type {import('./config.mjs').PrettifyBruConfig} */
|
|
39
|
+
const config = {
|
|
40
|
+
...defaultConfig,
|
|
41
|
+
...configOverrides,
|
|
42
|
+
prettier: Object.assign(
|
|
43
|
+
{},
|
|
44
|
+
defaultPrettierConfig,
|
|
45
|
+
Object.hasOwn(configOverrides, 'prettier') ? configOverrides.prettier : {}
|
|
46
|
+
),
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
let fileOutcome = {
|
|
40
50
|
newContents: originalContents.replace(/\r\n/g, '\n'),
|
|
41
51
|
blocksSearchedFor: 0,
|
|
@@ -43,23 +53,10 @@ export async function format(originalContents, only = null, config = {}) {
|
|
|
43
53
|
errorMessages: [],
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
const
|
|
47
|
-
{},
|
|
48
|
-
defaultPrettierConfig,
|
|
49
|
-
Object.hasOwn(config, 'prettier') ? config.prettier : {}
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
let i
|
|
53
|
-
for (i in formattableBlocks) {
|
|
54
|
-
let [blockName, parser] = formattableBlocks[i]
|
|
56
|
+
for (const blockName of formattableBlocks) {
|
|
55
57
|
if (only !== null && !blockName.includes(only)) continue
|
|
56
58
|
|
|
57
|
-
const blockOutcome = await formatBlock(
|
|
58
|
-
fileOutcome.newContents,
|
|
59
|
-
blockName,
|
|
60
|
-
parser,
|
|
61
|
-
prettierConfig
|
|
62
|
-
)
|
|
59
|
+
const blockOutcome = await formatBlock(fileOutcome.newContents, blockName, config)
|
|
63
60
|
fileOutcome.blocksSearchedFor++
|
|
64
61
|
if (blockOutcome.errorMessage !== null) {
|
|
65
62
|
fileOutcome.errorMessages.push(blockOutcome.errorMessage)
|
|
@@ -69,7 +66,16 @@ export async function format(originalContents, only = null, config = {}) {
|
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
formattableBlocks.forEach(blockName => {
|
|
70
|
+
const emptyBlockOutcome = stripEmptyBlock(fileOutcome.newContents, blockName)
|
|
71
|
+
|
|
72
|
+
if (emptyBlockOutcome.changeable) {
|
|
73
|
+
fileOutcome.changeable = true
|
|
74
|
+
fileOutcome.newContents = emptyBlockOutcome.fileContents
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
if (only === null && config.agnosticFilePaths) {
|
|
73
79
|
const fileBodyBlockOutcome = formatFilePaths(fileOutcome.newContents)
|
|
74
80
|
if (fileBodyBlockOutcome.errorMessage !== null) {
|
|
75
81
|
fileOutcome.errorMessages.push(fileBodyBlockOutcome.errorMessage)
|
|
@@ -91,11 +97,10 @@ export async function format(originalContents, only = null, config = {}) {
|
|
|
91
97
|
/**
|
|
92
98
|
* @param {string} fileContents
|
|
93
99
|
* @param {string} blockName
|
|
94
|
-
* @param {
|
|
95
|
-
* @param {Object} prettierConfig
|
|
100
|
+
* @param {import('./config.mjs').PrettifyBruConfig} config
|
|
96
101
|
* @returns {Promise<{fileContents: string, changeable: boolean, errorMessage: ?string}>}
|
|
97
102
|
*/
|
|
98
|
-
async function formatBlock(fileContents, blockName,
|
|
103
|
+
async function formatBlock(fileContents, blockName, config) {
|
|
99
104
|
let outcome = {fileContents, changeable: false, errorMessage: null}
|
|
100
105
|
|
|
101
106
|
const blockBodyRegex = new RegExp('\n' + blockName + ' [{]\\n(.+?)\\n}\\n', 's')
|
|
@@ -108,27 +113,27 @@ async function formatBlock(fileContents, blockName, parser, prettierConfig) {
|
|
|
108
113
|
// Remove 2-spaces of indentation, added due to being inside a Bru lang block
|
|
109
114
|
let unindented = rawBody.replace(/^ /gm, '')
|
|
110
115
|
|
|
111
|
-
if (
|
|
112
|
-
unindented =
|
|
116
|
+
if (config.shortenGetters && ['script:post-response', 'tests'].includes(blockName)) {
|
|
117
|
+
unindented = shortenGetters(unindented)
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
let
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
prettierFormatted = await prettier.format(
|
|
119
|
-
unindented,
|
|
120
|
-
Object.assign({}, prettierConfig, {parser})
|
|
121
|
-
)
|
|
122
|
-
} catch (e) {
|
|
123
|
-
outcome.errorMessage = `Prettier could not format ${blockName} because...\n${e.message}`
|
|
124
|
-
return outcome
|
|
125
|
-
}
|
|
120
|
+
let reformatted
|
|
126
121
|
|
|
127
122
|
if (blockName === 'body:json') {
|
|
128
|
-
|
|
123
|
+
unindented = wrapNonStringPlaceholdersInDelimiters(unindented)
|
|
124
|
+
const edits = jsoncFormat(unindented, undefined, {tabSize: 2, insertSpaces: true})
|
|
125
|
+
reformatted = applyEdits(unindented, edits)
|
|
126
|
+
reformatted = unwrapDelimitedPlaceholders(reformatted)
|
|
127
|
+
} else {
|
|
128
|
+
try {
|
|
129
|
+
reformatted = await prettier.format(unindented, config.prettier)
|
|
130
|
+
} catch (e) {
|
|
131
|
+
outcome.errorMessage = `Prettier could not format ${blockName} because...\n${e.message}`
|
|
132
|
+
return outcome
|
|
133
|
+
}
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
const bodyLines =
|
|
136
|
+
const bodyLines = reformatted.split('\n')
|
|
132
137
|
|
|
133
138
|
// Remove leading/trailing empty lines
|
|
134
139
|
while (bodyLines.length && bodyLines[0].trim() === '') bodyLines.shift()
|
|
@@ -149,6 +154,20 @@ async function formatBlock(fileContents, blockName, parser, prettierConfig) {
|
|
|
149
154
|
return outcome
|
|
150
155
|
}
|
|
151
156
|
|
|
157
|
+
/**
|
|
158
|
+
* @param {string} blockContents
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function shortenGetters(blockContents) {
|
|
162
|
+
const props = ['body', 'headers', 'responseTime', 'status', 'statusText', 'url']
|
|
163
|
+
props.forEach(prop => {
|
|
164
|
+
const getter = 'get' + prop.substring(0, 1).toUpperCase() + prop.substring(1)
|
|
165
|
+
const getterRegex = new RegExp('(?<!\\w)res\.' + getter + '\\(\\)', 'g')
|
|
166
|
+
blockContents = blockContents.replaceAll(getterRegex, `res.${prop}`)
|
|
167
|
+
})
|
|
168
|
+
return blockContents
|
|
169
|
+
}
|
|
170
|
+
|
|
152
171
|
/**
|
|
153
172
|
* Turns Bruno variable placeholders into strings with special delimiters, effectively making it valid JSON
|
|
154
173
|
*
|
|
@@ -171,7 +190,25 @@ function unwrapDelimitedPlaceholders(jsonBlock) {
|
|
|
171
190
|
|
|
172
191
|
/**
|
|
173
192
|
* @param {string} fileContents
|
|
174
|
-
* @
|
|
193
|
+
* @param {string} blockName
|
|
194
|
+
* @returns {{fileContents: string, changeable: boolean}}
|
|
195
|
+
*/
|
|
196
|
+
function stripEmptyBlock(fileContents, blockName) {
|
|
197
|
+
const emptyBlockRegex = new RegExp('\n' + blockName + ' [{]\\n}\\n', 's')
|
|
198
|
+
|
|
199
|
+
if (fileContents.match(emptyBlockRegex) !== null) {
|
|
200
|
+
return {
|
|
201
|
+
fileContents: fileContents.replace(emptyBlockRegex, ''),
|
|
202
|
+
changeable: true,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {fileContents, changeable: false}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {string} fileContents
|
|
211
|
+
* @returns {{fileContents: string, changeable: boolean, errorMessage: ?string}}
|
|
175
212
|
*/
|
|
176
213
|
function formatFilePaths(fileContents) {
|
|
177
214
|
let changeable = false
|
|
@@ -192,7 +229,7 @@ function formatFilePaths(fileContents) {
|
|
|
192
229
|
|
|
193
230
|
/**
|
|
194
231
|
* @param {string} fileContents
|
|
195
|
-
* @returns {fileContents: string, changeable: boolean}
|
|
232
|
+
* @returns {{fileContents: string, changeable: boolean}}
|
|
196
233
|
*/
|
|
197
234
|
function formatOverallStructure(fileContents) {
|
|
198
235
|
if (fileContents.match(/\n[}]\n[\s]+\n\w+/s) === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prettify-bru",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Prettifies JSON and JavaScript blocks in Bruno .bru files",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bruno",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"bin": "./cli.js",
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"jsonc-parser": "^3.3.1",
|
|
31
32
|
"prettier": "^3.3.3",
|
|
32
33
|
"yargs": "18.0.0"
|
|
33
34
|
},
|