From e8291b03fa3f3210795b808f40b9a11968f2d988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iiro=20J=C3=A4ppinen?= Date: Sun, 9 Jan 2022 11:02:48 +0200 Subject: [PATCH] feat: expose `--max-arg-length` cli option --- bin/lint-staged.js | 21 +------ lib/index.js | 20 ++++++- test/index3.spec.js | 142 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 20 deletions(-) create mode 100644 test/index3.spec.js diff --git a/bin/lint-staged.js b/bin/lint-staged.js index 9a6f6ebd9..de0ca6e5f 100755 --- a/bin/lint-staged.js +++ b/bin/lint-staged.js @@ -34,6 +34,7 @@ cmdline .option('-c, --config [path]', 'path to configuration file, or - to read from stdin') .option('--cwd [path]', 'run all tasks in specific directory, instead of the current') .option('-d, --debug', 'print additional debug information', false) + .option('--max-arg-length', 'maximum length of the command-line argument string') .option('--no-stash', 'disable the backup stash, and do not revert in case of errors', false) .option('-q, --quiet', 'disable lint-staged’s own console output', false) .option('-r, --relative', 'pass relative filepaths to tasks', false) @@ -54,31 +55,13 @@ if (cmdlineOptions.debug) { const debugLog = debug('lint-staged:bin') debugLog('Running `lint-staged@%s`', version) -/** - * Get the maximum length of a command-line argument string based on current platform - * - * https://serverfault.com/questions/69430/what-is-the-maximum-length-of-a-command-line-in-mac-os-x - * https://support.microsoft.com/en-us/help/830473/command-prompt-cmd-exe-command-line-string-limitation - * https://unix.stackexchange.com/a/120652 - */ -const getMaxArgLength = () => { - switch (process.platform) { - case 'darwin': - return 262144 - case 'win32': - return 8191 - default: - return 131072 - } -} - const options = { allowEmpty: !!cmdlineOptions.allowEmpty, concurrent: JSON.parse(cmdlineOptions.concurrent), configPath: cmdlineOptions.config, cwd: cmdlineOptions.cwd, debug: !!cmdlineOptions.debug, - maxArgLength: getMaxArgLength() / 2, + maxArgLength: JSON.parse(cmdlineOptions.maxArgLength || null), quiet: !!cmdlineOptions.quiet, relative: !!cmdlineOptions.relative, shell: cmdlineOptions.shell /* Either a boolean or a string pointing to the shell */, diff --git a/lib/index.js b/lib/index.js index 40c2d5c51..e15616d49 100644 --- a/lib/index.js +++ b/lib/index.js @@ -18,6 +18,24 @@ import { validateOptions } from './validateOptions.js' const debugLog = debug('lint-staged') +/** + * Get the maximum length of a command-line argument string based on current platform + * + * https://serverfault.com/questions/69430/what-is-the-maximum-length-of-a-command-line-in-mac-os-x + * https://support.microsoft.com/en-us/help/830473/command-prompt-cmd-exe-command-line-string-limitation + * https://unix.stackexchange.com/a/120652 + */ +const getMaxArgLength = () => { + switch (process.platform) { + case 'darwin': + return 262144 + case 'win32': + return 8191 + default: + return 131072 + } +} + /** * @typedef {(...any) => void} LogFunction * @typedef {{ error: LogFunction, log: LogFunction, warn: LogFunction }} Logger @@ -49,7 +67,7 @@ const lintStaged = async ( configPath, cwd, debug = false, - maxArgLength, + maxArgLength = getMaxArgLength() / 2, quiet = false, relative = false, shell = false, diff --git a/test/index3.spec.js b/test/index3.spec.js new file mode 100644 index 000000000..36697799a --- /dev/null +++ b/test/index3.spec.js @@ -0,0 +1,142 @@ +import makeConsoleMock from 'consolemock' + +import lintStaged from '../lib/index' +import { runAll } from '../lib/runAll' +import { getInitialState } from '../lib/state' +import { ApplyEmptyCommitError, ConfigNotFoundError, GitError } from '../lib/symbols' + +jest.mock('../lib/validateOptions.js', () => ({ + validateOptions: jest.fn(async () => {}), +})) + +jest.mock('../lib/runAll.js', () => ({ + runAll: jest.fn(async () => {}), +})) + +describe('lintStaged', () => { + it('should log error when configuration not found', async () => { + const ctx = getInitialState() + ctx.errors.add(ConfigNotFoundError) + runAll.mockImplementationOnce(async () => { + throw { ctx } + }) + + const logger = makeConsoleMock() + + await expect(lintStaged({}, logger)).resolves.toEqual(false) + + expect(logger.printHistory()).toMatchInlineSnapshot(` + " + ERROR ✖ No valid configuration found." + `) + }) + + it('should log error when preventing empty commit', async () => { + const ctx = getInitialState() + ctx.errors.add(ApplyEmptyCommitError) + runAll.mockImplementationOnce(async () => { + throw { ctx } + }) + + const logger = makeConsoleMock() + + await expect(lintStaged({}, logger)).resolves.toEqual(false) + + expect(logger.printHistory()).toMatchInlineSnapshot(` + " + WARN + ⚠ lint-staged prevented an empty git commit. + Use the --allow-empty option to continue, or check your task configuration + " + `) + }) + + it('should log error when preventing empty commit', async () => { + const ctx = getInitialState() + ctx.errors.add(ApplyEmptyCommitError) + runAll.mockImplementationOnce(async () => { + throw { ctx } + }) + + const logger = makeConsoleMock() + + await expect(lintStaged({}, logger)).resolves.toEqual(false) + + expect(logger.printHistory()).toMatchInlineSnapshot(` + " + WARN + ⚠ lint-staged prevented an empty git commit. + Use the --allow-empty option to continue, or check your task configuration + " + `) + }) + + it('should log error when a git operation failed', async () => { + const ctx = getInitialState() + ctx.shouldBackup = true + ctx.errors.add(GitError) + runAll.mockImplementationOnce(async () => { + throw { ctx } + }) + + const logger = makeConsoleMock() + + await expect(lintStaged({}, logger)).resolves.toEqual(false) + + expect(logger.printHistory()).toMatchInlineSnapshot(` + " + ERROR + ✖ lint-staged failed due to a git error. + ERROR Any lost modifications can be restored from a git stash: + + > git stash list + stash@{0}: automatic lint-staged backup + > git stash apply --index stash@{0} + " + `) + }) + + it('should throw when context is malformed', async () => { + expect.assertions(2) + + const testError = Symbol() + + runAll.mockImplementationOnce(async () => { + throw testError + }) + + const logger = makeConsoleMock() + + await lintStaged({}, logger).catch((error) => { + expect(error).toEqual(testError) + }) + + expect(logger.printHistory()).toMatchInlineSnapshot(`""`) + }) + + it.each` + platform | maxArgLength + ${'darwin'} | ${262144 / 2} + ${'win32'} | ${8191 / 2} + ${'others'} | ${131072 / 2} + `( + 'should use default max arg length of $maxArgLength on $platform', + async ({ platform, maxArgLength }) => { + const realPlatform = process.platform + Object.defineProperty(process, 'platform', { + value: platform, + }) + + await lintStaged({}, makeConsoleMock()) + + expect(runAll).toHaveBeenLastCalledWith( + expect.objectContaining({ maxArgLength }), + expect.objectContaining({}) + ) + + Object.defineProperty(process, 'platform', { + value: realPlatform, + }) + } + ) +})