@depup/lint-staged 16.3.2-depup.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.
@@ -0,0 +1,95 @@
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 getIncorrectBracesRegexp = () =>
32
+ /(?<![\\$])({)(?:(?!(?<!\\),|\.\.|\{|\}).)*?(?<!\\)(})/g
33
+
34
+ /**
35
+ * @param {string} pattern
36
+ * @returns {string}
37
+ */
38
+ const stripIncorrectBraces = (pattern) => {
39
+ let output = `${pattern}`
40
+ const regexp = getIncorrectBracesRegexp()
41
+ let match = regexp.exec(output)
42
+
43
+ while (match) {
44
+ const fullMatch = match[0]
45
+ const withoutBraces = fullMatch.replace(/{/, '').replace(/}/, '')
46
+ output = output.replace(fullMatch, withoutBraces)
47
+ regexp.lastIndex = 0 // restart iteration
48
+ match = regexp.exec(output)
49
+ }
50
+
51
+ return output
52
+ }
53
+
54
+ /**
55
+ * This RegExp matches "duplicate" opening and closing braces, without any other braces
56
+ * in between, where the duplication is redundant and should be removed.
57
+ *
58
+ * @example *.{{js,ts}} // should just be *.{js,ts}
59
+ */
60
+ export const DOUBLE_BRACES_REGEXP = /{{[^}{]*}}/
61
+
62
+ /**
63
+ * @param {string} pattern
64
+ * @returns {string}
65
+ */
66
+ const stripDoubleBraces = (pattern) => {
67
+ let output = `${pattern}`
68
+ const match = DOUBLE_BRACES_REGEXP.exec(pattern)?.[0]
69
+
70
+ if (match) {
71
+ const withoutBraces = match.replace('{{', '{').replace('}}', '}')
72
+ output = output.replace(match, withoutBraces)
73
+ }
74
+
75
+ return output
76
+ }
77
+
78
+ /**
79
+ * Validate and remove incorrect brace expansions from glob pattern.
80
+ * For example `*.{js}` is incorrect because it doesn't contain a `,` or `..`,
81
+ * and will be reformatted as `*.js`.
82
+ *
83
+ * @param {string} pattern the glob pattern
84
+ * @param {*} logger
85
+ * @returns {string}
86
+ */
87
+ export const validateBraces = (pattern, logger) => {
88
+ const fixedPattern = stripDoubleBraces(stripIncorrectBraces(pattern))
89
+
90
+ if (fixedPattern !== pattern) {
91
+ logger.warn(incorrectBraces(pattern, fixedPattern))
92
+ }
93
+
94
+ return fixedPattern
95
+ }
@@ -0,0 +1,108 @@
1
+ /** @typedef {import('./index').Logger} Logger */
2
+
3
+ import { inspect } from 'node:util'
4
+
5
+ import { createDebug } from './debug.js'
6
+ import { configurationError, failedToParseConfig } from './messages.js'
7
+ import { ConfigEmptyError, ConfigFormatError } from './symbols.js'
8
+ import { validateBraces } from './validateBraces.js'
9
+
10
+ const debugLog = createDebug('lint-staged:validateConfig')
11
+
12
+ export const validateConfigLogic = (config, configPath, logger) => {
13
+ debugLog('Validating config from `%s`...', configPath)
14
+
15
+ if (!config || (typeof config !== 'object' && typeof config !== 'function')) {
16
+ throw ConfigFormatError
17
+ }
18
+
19
+ /**
20
+ * Function configurations receive all staged files as their argument.
21
+ * They are not further validated here to make sure the function gets
22
+ * evaluated only once.
23
+ *
24
+ * @see getSpawnedTasks
25
+ */
26
+ if (typeof config === 'function') {
27
+ return { '*': config }
28
+ }
29
+
30
+ if (Object.entries(config).length === 0) {
31
+ throw ConfigEmptyError
32
+ }
33
+
34
+ const errors = []
35
+
36
+ /**
37
+ * Create a new validated config because the keys (patterns) might change.
38
+ * Since the Object.reduce method already loops through each entry in the config,
39
+ * it can be used for validating the values at the same time.
40
+ */
41
+ const validatedConfig = Object.entries(config).reduce((collection, [pattern, task]) => {
42
+ if (Array.isArray(task)) {
43
+ /** Array with invalid values */
44
+ if (task.some((item) => typeof item !== 'string' && typeof item !== 'function')) {
45
+ errors.push(
46
+ configurationError(pattern, 'Should be an array of strings or functions.', task)
47
+ )
48
+ }
49
+ } else if (typeof task === 'object') {
50
+ /** Invalid function task */
51
+ if (typeof task.title !== 'string' || typeof task.task !== 'function') {
52
+ errors.push(
53
+ configurationError(
54
+ pattern,
55
+ 'Function task should contain `title` and `task` fields, where `title` should be a string and `task` should be a function.',
56
+ task
57
+ )
58
+ )
59
+ }
60
+ } else if (typeof task !== 'string' && typeof task !== 'function') {
61
+ /** Singular invalid value */
62
+ errors.push(
63
+ configurationError(
64
+ pattern,
65
+ 'Should be a string, a function, an object or an array of strings and functions.',
66
+ task
67
+ )
68
+ )
69
+ }
70
+
71
+ /**
72
+ * A typical configuration error is using invalid brace expansion, like `*.{js}`.
73
+ * These are automatically fixed and warned about.
74
+ */
75
+ const fixedPattern = validateBraces(pattern, logger)
76
+
77
+ return Object.assign(collection, { [fixedPattern]: task })
78
+ }, {})
79
+
80
+ if (errors.length) {
81
+ const message = errors.join('\n\n')
82
+
83
+ logger.error(failedToParseConfig(configPath, message))
84
+
85
+ throw new Error(message)
86
+ }
87
+
88
+ debugLog('Validated config from `%s`:', configPath)
89
+ debugLog(inspect(config, { compact: false }))
90
+
91
+ return validatedConfig
92
+ }
93
+
94
+ /**
95
+ * Runs config validation. Throws error if the config is not valid.
96
+ * @param {Object} config
97
+ * @param {string} configPath
98
+ * @param {Logger} logger
99
+ * @returns {Object} config
100
+ */
101
+ export const validateConfig = (config, configPath, logger) => {
102
+ try {
103
+ return validateConfigLogic(config, configPath, logger)
104
+ } catch (error) {
105
+ logger.error(failedToParseConfig(configPath, error))
106
+ throw error
107
+ }
108
+ }
@@ -0,0 +1,33 @@
1
+ import { constants } from 'node:fs'
2
+ import fs from 'node:fs/promises'
3
+ import path from 'node:path'
4
+
5
+ import { createDebug } from './debug.js'
6
+ import { invalidOption } from './messages.js'
7
+ import { InvalidOptionsError } from './symbols.js'
8
+
9
+ const debugLog = createDebug('lint-staged:validateOptions')
10
+
11
+ /**
12
+ * Validate lint-staged options, either from the Node.js API or the command line flags.
13
+ * @param {*} options
14
+ * @param {boolean|string} [options.cwd] - Current working directory
15
+ * @throws {InvalidOptionsError}
16
+ */
17
+ export const validateOptions = async (options = {}, logger) => {
18
+ debugLog('Validating options...')
19
+
20
+ /** Ensure the passed cwd option exists; it might also be relative */
21
+ if (typeof options.cwd === 'string') {
22
+ try {
23
+ const resolved = path.resolve(options.cwd)
24
+ await fs.access(resolved, constants.F_OK)
25
+ } catch (error) {
26
+ debugLog('Failed to validate options: %o', options)
27
+ logger.error(invalidOption('cwd', options.cwd, error.message))
28
+ throw InvalidOptionsError
29
+ }
30
+ }
31
+
32
+ debugLog('Validated options: %o', options)
33
+ }
package/lib/version.js ADDED
@@ -0,0 +1,6 @@
1
+ import fs from 'node:fs/promises'
2
+
3
+ export const getVersion = async () => {
4
+ const packageJson = JSON.parse(await fs.readFile(new URL('../package.json', import.meta.url)))
5
+ return packageJson.version
6
+ }
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@depup/lint-staged",
3
+ "version": "16.3.2-depup.0",
4
+ "description": "Lint files staged by git",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/lint-staged/lint-staged.git"
9
+ },
10
+ "homepage": "https://github.com/lint-staged/lint-staged#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/lint-staged/lint-staged/issues"
13
+ },
14
+ "author": "Andrey Okonetchnikov <andrey@okonet.ru>",
15
+ "maintainers": [
16
+ "Iiro Jäppinen <iiro@jappinen.fi> (https://iiro.fi)"
17
+ ],
18
+ "funding": {
19
+ "url": "https://opencollective.com/lint-staged"
20
+ },
21
+ "engines": {
22
+ "node": ">=20.17"
23
+ },
24
+ "type": "module",
25
+ "bin": {
26
+ "lint-staged": "bin/lint-staged.js"
27
+ },
28
+ "exports": {
29
+ ".": {
30
+ "types": "./lib/index.d.ts",
31
+ "default": "./lib/index.js"
32
+ },
33
+ "./bin": "./bin/lint-staged.js",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "types": "./lib/index.d.ts",
37
+ "files": [
38
+ "bin/",
39
+ "lib/",
40
+ "MIGRATION.md"
41
+ ],
42
+ "scripts": {
43
+ "lint": "eslint",
44
+ "test": "vitest",
45
+ "typecheck": "tsc",
46
+ "version": "npx changeset version",
47
+ "postversion": "npm i --package-lock-only && git commit -am \"chore(changeset): release\"",
48
+ "tag": "npx changeset tag"
49
+ },
50
+ "dependencies": {
51
+ "commander": "^14.0.3",
52
+ "listr2": "^10.2.1",
53
+ "micromatch": "^4.0.8",
54
+ "string-argv": "^0.3.2",
55
+ "tinyexec": "^1.0.2",
56
+ "yaml": "^2.8.2"
57
+ },
58
+ "devDependencies": {
59
+ "@changesets/changelog-github": "0.5.2",
60
+ "@changesets/cli": "2.29.8",
61
+ "@commitlint/cli": "20.4.2",
62
+ "@commitlint/config-conventional": "20.4.2",
63
+ "@eslint/js": "10.0.1",
64
+ "@vitest/coverage-v8": "4.0.18",
65
+ "@vitest/eslint-plugin": "1.6.9",
66
+ "consolemock": "1.1.0",
67
+ "cross-env": "10.1.0",
68
+ "eslint": "10.0.2",
69
+ "eslint-config-prettier": "10.1.8",
70
+ "eslint-plugin-n": "17.24.0",
71
+ "eslint-plugin-prettier": "5.5.5",
72
+ "eslint-plugin-simple-import-sort": "12.1.1",
73
+ "husky": "9.1.7",
74
+ "mock-stdin": "1.0.0",
75
+ "prettier": "3.8.1",
76
+ "semver": "7.7.4",
77
+ "typescript": "5.9.3",
78
+ "vitest": "4.0.18"
79
+ },
80
+ "keywords": [
81
+ "lint",
82
+ "git",
83
+ "staged",
84
+ "eslint",
85
+ "prettier",
86
+ "stylelint",
87
+ "code",
88
+ "quality",
89
+ "check",
90
+ "format",
91
+ "validate"
92
+ ]
93
+ }