1 | import { incorrectBraces } from './messages.js'
|
2 |
|
3 | /**
|
4 | * A correctly-formed brace expansion must contain unquoted opening and closing braces,
|
5 | * and at least one unquoted comma or a valid sequence expression.
|
6 | * Any incorrectly formed brace expansion is left unchanged.
|
7 | *
|
8 | * @see https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html
|
9 | *
|
10 | * Lint-staged uses `micromatch` for brace expansion, and its behavior is to treat
|
11 | * invalid brace expansions as literal strings, which means they (typically) do not match
|
12 | * anything.
|
13 | *
|
14 | * This RegExp tries to match most cases of invalid brace expansions, so that they can be
|
15 | * detected, warned about, and re-formatted by removing the braces and thus hopefully
|
16 | * matching the files as intended by the user. The only real fix is to remove the incorrect
|
17 | * braces from user configuration, but this is left to the user (after seeing the warning).
|
18 | *
|
19 | * @example <caption>Globs with brace expansions</caption>
|
20 | * - *.{js,tx} // expanded as *.js, *.ts
|
21 | * - *.{{j,t}s,css} // expanded as *.js, *.ts, *.css
|
22 | * - file_{1..10}.css // expanded as file_1.css, file_2.css, …, file_10.css
|
23 | *
|
24 | * @example <caption>Globs with incorrect brace expansions</caption>
|
25 | * - *.{js} // should just be *.js
|
26 | * - *.{js,{ts}} // should just be *.{js,ts}
|
27 | * - *.\{js\} // escaped braces, so they're treated literally
|
28 | * - *.${js} // dollar-sign inhibits expansion, so treated literally
|
29 | * - *.{js\,ts} // the comma is escaped, so treated literally
|
30 | */
|
31 | export const BRACES_REGEXP = /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
|
32 |
|
33 | /**
|
34 | * @param {string} pattern
|
35 | * @returns {string}
|
36 | */
|
37 | const withoutIncorrectBraces = (pattern) => {
|
38 | let output = `${pattern}`
|
39 | let match = null
|
40 |
|
41 | while ((match = BRACES_REGEXP.exec(pattern))) {
|
42 | const fullMatch = match[0]
|
43 | const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
|
44 | output = output.replace(fullMatch, withoutBraces)
|
45 | }
|
46 |
|
47 | return output
|
48 | }
|
49 |
|
50 | /**
|
51 | * Validate and remove incorrect brace expansions from glob pattern.
|
52 | * For example `*.{js}` is incorrect because it doesn't contain a `,` or `..`,
|
53 | * and will be reformatted as `*.js`.
|
54 | *
|
55 | * @param {string} pattern the glob pattern
|
56 | * @param {*} logger
|
57 | * @returns {string}
|
58 | */
|
59 | export const validateBraces = (pattern, logger) => {
|
60 | const fixedPattern = withoutIncorrectBraces(pattern)
|
61 |
|
62 | if (fixedPattern !== pattern) {
|
63 | logger.warn(incorrectBraces(pattern, fixedPattern))
|
64 | }
|
65 |
|
66 | return fixedPattern
|
67 | }
|