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 +36 -24
- package/lib/files.mjs +16 -16
- package/lib/format.mjs +49 -27
- package/lib/main.mjs +34 -31
- package/package.json +4 -2
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(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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: [
|
|
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
|
-
|
|
48
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
29
|
-
if (p.endsWith(
|
|
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([
|
|
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:
|
|
11
|
-
endOfLine:
|
|
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
|
-
*
|
|
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:
|
|
26
|
+
* @returns {Promise<{newContents: string, blocksSearchedFor: number, changeable: boolean, errorMessages: string[]}>}
|
|
27
27
|
*/
|
|
28
|
-
export async function
|
|
29
|
-
|
|
28
|
+
export async function format(originalContents, only = null) {
|
|
30
29
|
let fileOutcome = {
|
|
31
|
-
newContents: originalContents.replace(/\r\n/g,
|
|
30
|
+
newContents: originalContents.replace(/\r\n/g, '\n'),
|
|
32
31
|
blocksSearchedFor: 0,
|
|
33
|
-
changeable:
|
|
34
|
-
|
|
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.
|
|
45
|
-
fileOutcome.
|
|
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,
|
|
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
|
|
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(
|
|
82
|
+
prettierFormatted = await prettier.format(
|
|
83
|
+
unindented,
|
|
84
|
+
Object.assign(prettierConfig, {parser})
|
|
85
|
+
)
|
|
79
86
|
} catch (e) {
|
|
80
|
-
outcome.
|
|
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(
|
|
91
|
+
const bodyLines = prettierFormatted.split('\n')
|
|
85
92
|
|
|
86
93
|
// Remove leading/trailing empty lines
|
|
87
|
-
while (bodyLines.length && bodyLines[0].trim() ===
|
|
88
|
-
while (bodyLines.length && bodyLines[bodyLines.length - 1].trim() ===
|
|
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(
|
|
98
|
+
const indentedLines = bodyLines.map(l => ' ' + l)
|
|
92
99
|
|
|
93
|
-
const formattedBody = indentedLines.join(
|
|
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 {
|
|
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(
|
|
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.
|
|
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
|
|
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(
|
|
52
|
-
|
|
53
|
-
|
|
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(
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
87
|
+
const fileOutcome = await format(original, only)
|
|
85
88
|
|
|
86
|
-
if (write && fileOutcome.changeable
|
|
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
|
+
"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",
|