lint-staged 12.0.3 → 12.1.3

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 CHANGED
@@ -106,12 +106,15 @@ Starting with v3.1 you can now use different ways of configuring lint-staged:
106
106
  - `.lintstagedrc.json`
107
107
  - `.lintstagedrc.yaml`
108
108
  - `.lintstagedrc.yml`
109
- - `lint-staged.config.js`, `.lintstagedrc.js`, or `.lintstagedrc.cjs` file in JS format
109
+ - `.lintstagedrc.mjs` or `lint-staged.config.mjs` file in ESM format
110
+ - the default export value should be a configuration: `export default { ... }`
111
+ - `.lintstagedrc.cjs` or `lint-staged.config.cjs` file in CommonJS format
112
+ - the exports value should be a configuration: `module.exports = { ... }`
113
+ - `lint-staged.config.js` or `.lintstagedrc.js` in either ESM or CommonJS format, depending on
114
+ whether your project's _package.json_ contains the `"type": "module"` option or not.
110
115
  - Pass a configuration file using the `--config` or `-c` flag
111
116
 
112
- See [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) for more details on what formats are supported.
113
-
114
- Configuration should be an object where each value is a command to run and its key is a glob pattern to use for this command. This package uses [micromatch](https://github.com/micromatch/micromatch) for glob patterns.
117
+ Configuration should be an object where each value is a command to run and its key is a glob pattern to use for this command. This package uses [micromatch](https://github.com/micromatch/micromatch) for glob patterns. JavaScript files can also export advanced configuration as a function. See [Using JS configuration files](#using-js-configuration-files) for more info.
115
118
 
116
119
  #### `package.json` example:
117
120
 
@@ -197,7 +200,7 @@ For example:
197
200
 
198
201
  going to execute `eslint` and if it exits with `0` code, it will execute `prettier --write` on all staged `*.js` files.
199
202
 
200
- ## Using JS configuration file
203
+ ## Using JS configuration files
201
204
 
202
205
  Writing the configuration file in JavaScript is the most powerful way to configure lint-staged (`lint-staged.config.js`, [similar](https://github.com/okonet/lint-staged/README.md#configuration), or passed via `--config`). From the configuration file, you can export either a single function or an object.
203
206
 
@@ -220,9 +223,9 @@ The function can also be async:
220
223
 
221
224
  ```js
222
225
  // lint-staged.config.js
223
- const micromatch = require('micromatch')
226
+ import micromatch from 'micromatch'
224
227
 
225
- module.exports = (allStagedFiles) => {
228
+ export default (allStagedFiles) => {
226
229
  const shFiles = micromatch(allStagedFiles, ['**/src/**/*.sh'])
227
230
  if (shFiles.length) {
228
231
  return `printf '%s\n' "Script files aren't allowed in src directory" >&2`
@@ -242,7 +245,7 @@ module.exports = (allStagedFiles) => {
242
245
 
243
246
  ```js
244
247
  // .lintstagedrc.js
245
- module.exports = {
248
+ export default {
246
249
  '**/*.js?(x)': (filenames) => filenames.map((filename) => `prettier --write '${filename}'`),
247
250
  }
248
251
  ```
@@ -256,7 +259,7 @@ module.exports = {
256
259
 
257
260
  ```js
258
261
  // lint-staged.config.js
259
- module.exports = {
262
+ export default {
260
263
  '**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
261
264
  }
262
265
  ```
@@ -270,7 +273,7 @@ module.exports = {
270
273
 
271
274
  ```js
272
275
  // .lintstagedrc.js
273
- module.exports = {
276
+ export default {
274
277
  '**/*.js?(x)': (filenames) =>
275
278
  filenames.length > 10 ? 'eslint .' : `eslint ${filenames.join(' ')}`,
276
279
  }
@@ -287,9 +290,9 @@ It's better to use the [function-based configuration (seen above)](https://githu
287
290
 
288
291
  ```js
289
292
  // lint-staged.config.js
290
- const micromatch = require('micromatch')
293
+ import micromatch from 'micromatch'
291
294
 
292
- module.exports = {
295
+ export default {
293
296
  '*': (allFiles) => {
294
297
  const codeFiles = micromatch(allFiles, ['**/*.js', '**/*.ts'])
295
298
  const docFiles = micromatch(allFiles, ['**/*.md'])
@@ -309,9 +312,9 @@ If for some reason you want to ignore files from the glob match, you can use `mi
309
312
 
310
313
  ```js
311
314
  // lint-staged.config.js
312
- const micromatch = require('micromatch')
315
+ import micromatch from 'micromatch'
313
316
 
314
- module.exports = {
317
+ export default {
315
318
  '*.js': (files) => {
316
319
  // from `files` filter those _NOT_ matching `*test.js`
317
320
  const match = micromatch.not(files, '*test.js')
@@ -330,9 +333,9 @@ Please note that for most cases, globs can achieve the same effect. For the abov
330
333
  <summary>Click to expand</summary>
331
334
 
332
335
  ```js
333
- const path = require('path')
336
+ import path from 'path'
334
337
 
335
- module.exports = {
338
+ export default {
336
339
  '*.ts': (absolutePaths) => {
337
340
  const cwd = process.cwd()
338
341
  const relativePaths = absolutePaths.map((file) => path.relative(cwd, file))
@@ -546,7 +549,7 @@ See more on [this blog post](https://medium.com/@tomchentw/imagemin-lint-staged-
546
549
  Yes!
547
550
 
548
551
  ```js
549
- const lintStaged = require('lint-staged')
552
+ import lintStaged from 'lint-staged'
550
553
 
551
554
  try {
552
555
  const success = await lintStaged()
@@ -685,13 +688,13 @@ Based on the discussion from [this issue](https://github.com/eslint/eslint/issue
685
688
  So you can setup a `.lintstagedrc.js` config file to do this:
686
689
 
687
690
  ```js
688
- const { CLIEngine } = require('eslint')
689
-
690
- const cli = new CLIEngine({})
691
+ import { CLIEngine } from 'eslint'
691
692
 
692
- module.exports = {
693
- '*.js': (files) =>
694
- 'eslint --max-warnings=0 ' + files.filter((file) => !cli.isPathIgnored(file)).join(' '),
693
+ export default {
694
+ '*.js': (files) => {
695
+ const cli = new CLIEngine({})
696
+ return 'eslint --max-warnings=0 ' + files.filter((file) => !cli.isPathIgnored(file)).join(' ')
697
+ },
695
698
  }
696
699
  ```
697
700
 
@@ -707,7 +710,7 @@ In versions of ESLint > 7, [isPathIgnored](https://eslint.org/docs/developer-gui
707
710
  Since [10.5.3](https://github.com/okonet/lint-staged/releases), any errors due to a bad ESLint config will come through to the console.
708
711
 
709
712
  ```js
710
- const { ESLint } = require('eslint')
713
+ import { ESLint } from 'eslint'
711
714
 
712
715
  const removeIgnoredFiles = async (files) => {
713
716
  const eslint = new ESLint()
@@ -720,7 +723,7 @@ const removeIgnoredFiles = async (files) => {
720
723
  return filteredFiles.join(' ')
721
724
  }
722
725
 
723
- module.exports = {
726
+ export default {
724
727
  '**/*.{ts,tsx,js,jsx}': async (files) => {
725
728
  const filesToLint = await removeIgnoredFiles(files)
726
729
  return [`eslint --max-warnings=0 ${filesToLint}`]
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { cosmiconfig } from 'cosmiconfig'
2
1
  import debug from 'debug'
3
2
  import inspect from 'object-inspect'
4
3
 
4
+ import { loadConfig } from './loadConfig.js'
5
5
  import { PREVENTED_EMPTY_COMMIT, GIT_ERROR, RESTORE_STASH_EXAMPLE } from './messages.js'
6
6
  import { printTaskOutput } from './printTaskOutput.js'
7
7
  import { runAll } from './runAll.js'
@@ -16,32 +16,6 @@ import { validateOptions } from './validateOptions.js'
16
16
 
17
17
  const debugLog = debug('lint-staged')
18
18
 
19
- const resolveConfig = (configPath) => {
20
- try {
21
- return require.resolve(configPath)
22
- } catch {
23
- return configPath
24
- }
25
- }
26
-
27
- const loadConfig = (configPath) => {
28
- const explorer = cosmiconfig('lint-staged', {
29
- searchPlaces: [
30
- 'package.json',
31
- '.lintstagedrc',
32
- '.lintstagedrc.json',
33
- '.lintstagedrc.yaml',
34
- '.lintstagedrc.yml',
35
- '.lintstagedrc.js',
36
- '.lintstagedrc.cjs',
37
- 'lint-staged.config.js',
38
- 'lint-staged.config.cjs',
39
- ],
40
- })
41
-
42
- return configPath ? explorer.load(resolveConfig(configPath)) : explorer.search()
43
- }
44
-
45
19
  /**
46
20
  * @typedef {(...any) => void} LogFunction
47
21
  * @typedef {{ error: LogFunction, log: LogFunction, warn: LogFunction }} Logger
@@ -84,22 +58,14 @@ const lintStaged = async (
84
58
  ) => {
85
59
  await validateOptions({ shell }, logger)
86
60
 
87
- debugLog('Loading config using `cosmiconfig`')
61
+ const inputConfig = configObject || (await loadConfig(configPath, logger))
88
62
 
89
- const resolved = configObject
90
- ? { config: configObject, filepath: '(input)' }
91
- : await loadConfig(configPath)
92
-
93
- if (resolved == null) {
63
+ if (!inputConfig) {
94
64
  logger.error(`${ConfigNotFoundError.message}.`)
95
65
  throw ConfigNotFoundError
96
66
  }
97
67
 
98
- debugLog('Successfully loaded config from `%s`:\n%O', resolved.filepath, resolved.config)
99
-
100
- // resolved.config is the parsed configuration object
101
- // resolved.filepath is the path to the config file that was found
102
- const config = validateConfig(resolved.config, logger)
68
+ const config = validateConfig(inputConfig, logger)
103
69
 
104
70
  if (debug) {
105
71
  // Log using logger to be able to test through `consolemock`.
@@ -0,0 +1,86 @@
1
+ import { pathToFileURL } from 'url'
2
+
3
+ import debug from 'debug'
4
+ import { lilconfig } from 'lilconfig'
5
+ import YAML from 'yaml'
6
+
7
+ const debugLog = debug('lint-staged:loadConfig')
8
+
9
+ /**
10
+ * The list of files `lint-staged` will read configuration
11
+ * from, in the declared order.
12
+ */
13
+ const searchPlaces = [
14
+ 'package.json',
15
+ '.lintstagedrc',
16
+ '.lintstagedrc.json',
17
+ '.lintstagedrc.yaml',
18
+ '.lintstagedrc.yml',
19
+ '.lintstagedrc.mjs',
20
+ '.lintstagedrc.js',
21
+ '.lintstagedrc.cjs',
22
+ 'lint-staged.config.mjs',
23
+ 'lint-staged.config.js',
24
+ 'lint-staged.config.cjs',
25
+ ]
26
+
27
+ /** exported for tests */
28
+ export const dynamicImport = (path) => import(pathToFileURL(path)).then((module) => module.default)
29
+
30
+ const jsonParse = (path, content) => JSON.parse(content)
31
+
32
+ const yamlParse = (path, content) => YAML.parse(content)
33
+
34
+ /**
35
+ * `lilconfig` doesn't support yaml files by default,
36
+ * so we add custom loaders for those. Files without
37
+ * an extensions are assumed to be yaml — this
38
+ * assumption is in `cosmiconfig` as well.
39
+ */
40
+ const loaders = {
41
+ '.js': dynamicImport,
42
+ '.json': jsonParse,
43
+ '.mjs': dynamicImport,
44
+ '.cjs': dynamicImport,
45
+ '.yaml': yamlParse,
46
+ '.yml': yamlParse,
47
+ noExt: yamlParse,
48
+ }
49
+
50
+ const resolveConfig = (configPath) => {
51
+ try {
52
+ return require.resolve(configPath)
53
+ } catch {
54
+ return configPath
55
+ }
56
+ }
57
+
58
+ /**
59
+ * @param {string} [configPath]
60
+ * @param {Logger} [logger]
61
+ */
62
+ export const loadConfig = async (configPath, logger) => {
63
+ try {
64
+ if (configPath) {
65
+ debugLog('Loading configuration from `%s`...', configPath)
66
+ } else {
67
+ debugLog('Searching for configuration...')
68
+ }
69
+
70
+ const explorer = lilconfig('lint-staged', { searchPlaces, loaders })
71
+
72
+ const result = await (configPath ? explorer.load(resolveConfig(configPath)) : explorer.search())
73
+ if (!result) return null
74
+
75
+ // config is a promise when using the `dynamicImport` loader
76
+ const config = await result.config
77
+
78
+ debugLog('Successfully loaded config from `%s`:\n%O', result.filepath, config)
79
+
80
+ return config
81
+ } catch (error) {
82
+ debugLog('Failed to load configuration from `%s`', configPath)
83
+ logger.error(error)
84
+ return null
85
+ }
86
+ }
package/lib/symbols.js CHANGED
@@ -1,6 +1,10 @@
1
1
  export const ApplyEmptyCommitError = Symbol('ApplyEmptyCommitError')
2
2
 
3
- export const ConfigNotFoundError = new Error('Config could not be found')
3
+ export const ConfigNotFoundError = new Error('Configuration could not be found')
4
+
5
+ export const ConfigFormatError = new Error('Configuration should be an object or a function')
6
+
7
+ export const ConfigEmptyError = new Error('Configuration should not be empty')
4
8
 
5
9
  export const GetBackupStashError = Symbol('GetBackupStashError')
6
10
 
@@ -1,6 +1,7 @@
1
1
  import debug from 'debug'
2
2
 
3
3
  import { configurationError } from './messages.js'
4
+ import { ConfigEmptyError, ConfigFormatError } from './symbols.js'
4
5
  import { validateBraces } from './validateBraces.js'
5
6
 
6
7
  const debugLog = debug('lint-staged:validateConfig')
@@ -27,7 +28,7 @@ export const validateConfig = (config, logger) => {
27
28
  debugLog('Validating config')
28
29
 
29
30
  if (!config || (typeof config !== 'object' && typeof config !== 'function')) {
30
- throw new Error('Configuration should be an object or a function!')
31
+ throw ConfigFormatError
31
32
  }
32
33
 
33
34
  /**
@@ -42,7 +43,7 @@ export const validateConfig = (config, logger) => {
42
43
  }
43
44
 
44
45
  if (Object.entries(config).length === 0) {
45
- throw new Error('Configuration should not be empty!')
46
+ throw ConfigEmptyError
46
47
  }
47
48
 
48
49
  const errors = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lint-staged",
3
- "version": "12.0.3",
3
+ "version": "12.1.3",
4
4
  "description": "Lint files staged by git",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/okonet/lint-staged",
@@ -24,7 +24,6 @@
24
24
  "lib"
25
25
  ],
26
26
  "scripts": {
27
- "cz": "git-cz",
28
27
  "lint": "eslint .",
29
28
  "test": "jest --coverage",
30
29
  "test:watch": "jest --watch"
@@ -33,39 +32,33 @@
33
32
  "cli-truncate": "^3.1.0",
34
33
  "colorette": "^2.0.16",
35
34
  "commander": "^8.3.0",
36
- "cosmiconfig": "^7.0.1",
37
- "debug": "^4.3.2",
38
- "enquirer": "^2.3.6",
35
+ "debug": "^4.3.3",
39
36
  "execa": "^5.1.1",
40
- "listr2": "^3.13.3",
37
+ "lilconfig": "2.0.4",
38
+ "listr2": "^3.13.5",
41
39
  "micromatch": "^4.0.4",
42
40
  "normalize-path": "^3.0.0",
43
- "object-inspect": "^1.11.0",
41
+ "object-inspect": "^1.11.1",
44
42
  "string-argv": "^0.3.1",
45
- "supports-color": "^9.0.2"
43
+ "supports-color": "^9.2.1",
44
+ "yaml": "^1.10.2"
46
45
  },
47
46
  "devDependencies": {
48
- "@babel/core": "^7.16.0",
49
- "@babel/eslint-parser": "^7.16.3",
50
- "@babel/preset-env": "^7.16.0",
51
- "babel-jest": "^27.3.1",
47
+ "@babel/core": "^7.16.5",
48
+ "@babel/eslint-parser": "^7.16.5",
49
+ "@babel/preset-env": "^7.16.5",
50
+ "babel-jest": "^27.4.5",
52
51
  "consolemock": "^1.1.0",
53
- "eslint": "^8.2.0",
52
+ "eslint": "^8.4.1",
54
53
  "eslint-config-prettier": "^8.3.0",
55
54
  "eslint-plugin-import": "^2.25.3",
56
55
  "eslint-plugin-node": "^11.1.0",
57
56
  "eslint-plugin-prettier": "^4.0.0",
58
57
  "fs-extra": "^10.0.0",
59
58
  "husky": "^7.0.4",
60
- "jest": "^27.3.1",
61
- "jest-mock-console": "^1.2.3",
59
+ "jest": "^27.4.5",
62
60
  "jest-snapshot-serializer-ansi": "^1.0.0",
63
- "prettier": "^2.4.1"
64
- },
65
- "config": {
66
- "commitizen": {
67
- "path": "./node_modules/cz-conventional-changelog"
68
- }
61
+ "prettier": "^2.5.1"
69
62
  },
70
63
  "keywords": [
71
64
  "lint",