lint-staged
Advanced tools
@@ -131,3 +131,3 @@ #!/usr/bin/env node | ||
| debugLog('Options parsed from command-line:', options) | ||
| debugLog('Options parsed from command-line: %o', options) | ||
@@ -134,0 +134,0 @@ if (options.configPath === '-') { |
@@ -34,3 +34,3 @@ import path from 'node:path' | ||
| * @param {number} [opts.maxArgLength] the maximum argument string length | ||
| * @param {Boolean} [opts.relative] whether files are relative to `gitDir` or should be resolved as absolute | ||
| * @param {Boolean} [opts.relative] whether files are relative to `topLevelDir` or should be resolved as absolute | ||
| * @returns {Array<Array<String>>} | ||
@@ -37,0 +37,0 @@ */ |
@@ -16,5 +16,4 @@ import path from 'node:path' | ||
| * @param {Object} [options.cwd] - Current working directory | ||
| * @param {boolean} [options.gitDir] - Git root directory | ||
| * @param {boolean} [options.files] - Staged filepaths | ||
| * @param {boolean} [options.relative] - Whether filepaths to should be relative to gitDir | ||
| * @param {boolean} [options.relative] - Whether filepaths to should be relative to cwd | ||
| */ | ||
@@ -21,0 +20,0 @@ export const generateTasks = ({ config, cwd = process.cwd(), files, relative = false }) => { |
@@ -69,7 +69,7 @@ import path from 'node:path' | ||
| export class GitWorkflow { | ||
| constructor({ allowEmpty, gitConfigDir, gitDir, matchedFileChunks, diff, diffFilter }) { | ||
| this.execGit = (args, options = {}) => execGit(args, { ...options, cwd: gitDir }) | ||
| constructor({ allowEmpty, gitConfigDir, topLevelDir, matchedFileChunks, diff, diffFilter }) { | ||
| this.execGit = (args, options = {}) => execGit(args, { ...options, cwd: topLevelDir }) | ||
| this.deletedFiles = [] | ||
| this.gitConfigDir = gitConfigDir | ||
| this.gitDir = gitDir | ||
| this.topLevelDir = topLevelDir | ||
| this.diff = diff | ||
@@ -120,3 +120,3 @@ this.diffFilter = diffFilter | ||
| .filter(Boolean) | ||
| .map((file) => path.resolve(this.gitDir, file)) | ||
| .map((file) => path.resolve(this.topLevelDir, file)) | ||
| debugLog('Found deleted files:', deletedFiles) | ||
@@ -123,0 +123,0 @@ return deletedFiles |
+9
-5
| import debug from 'debug' | ||
| import { execGit } from './execGit.js' | ||
| import { | ||
@@ -85,8 +86,5 @@ PREVENTED_EMPTY_COMMIT, | ||
| ) => { | ||
| await validateOptions({ cwd, shell }, logger) | ||
| const gitVersion = await execGit(['version', '--build-options'], { cwd }) | ||
| debugLog('%s', gitVersion) | ||
| // Unset GIT_LITERAL_PATHSPECS to not mess with path interpretation | ||
| debugLog('Unset GIT_LITERAL_PATHSPECS (was `%s`)', process.env.GIT_LITERAL_PATHSPECS) | ||
| delete process.env.GIT_LITERAL_PATHSPECS | ||
| const options = { | ||
@@ -110,2 +108,8 @@ allowEmpty, | ||
| await validateOptions(options, logger) | ||
| // Unset GIT_LITERAL_PATHSPECS to not mess with path interpretation | ||
| debugLog('Unset GIT_LITERAL_PATHSPECS (was `%s`)', process.env.GIT_LITERAL_PATHSPECS) | ||
| delete process.env.GIT_LITERAL_PATHSPECS | ||
| try { | ||
@@ -112,0 +116,0 @@ const ctx = await runAll(options, logger) |
@@ -15,7 +15,7 @@ import debug from 'debug' | ||
| * @param {Array<string>} options.files | ||
| * @param {string} options.gitDir | ||
| * @param {string} options.topLevelDir | ||
| * @param {Boolean} shell | ||
| * @param {Boolean} verbose | ||
| */ | ||
| export const makeCmdTasks = async ({ commands, cwd, files, gitDir, shell, verbose }) => { | ||
| export const makeCmdTasks = async ({ commands, cwd, files, topLevelDir, shell, verbose }) => { | ||
| debugLog('Creating listr tasks for commands %o', commands) | ||
@@ -47,3 +47,3 @@ const commandArray = Array.isArray(commands) ? commands : [commands] | ||
| const task = resolveTaskFn({ command, cwd, files, gitDir, isFn, shell, verbose }) | ||
| const task = resolveTaskFn({ command, cwd, files, topLevelDir, isFn, shell, verbose }) | ||
| cmdTasks.push({ title: command, command, task }) | ||
@@ -50,0 +50,0 @@ } |
@@ -0,1 +1,3 @@ | ||
| import path from 'node:path' | ||
| import debug from 'debug' | ||
@@ -9,4 +11,34 @@ | ||
| /** | ||
| * Resolve git directory and possible submodule paths | ||
| * Resolve .git directory relative to repo top-level directory | ||
| * | ||
| * @example ".git" | ||
| */ | ||
| const resolveRelativeGitDir = async (cwd = process.cwd()) => { | ||
| /** | ||
| * Absolute repo top-level directory | ||
| * | ||
| * @example <caption>Git on macOS</caption> | ||
| * "/Users/iiro/Documents/git/lint-staged" | ||
| * | ||
| * @example <caption>Git for Windows</caption> | ||
| * "C:\Users\iiro\Documents\git\lint-staged" | ||
| * | ||
| * @example <caption>Git installed with MSYS2, this doesn't work when used as CWD with Node.js child_process</caption> | ||
| * "/c/Users/iiro/Documents/git/lint-staged" | ||
| */ | ||
| const topLevelPromise = execGit(['rev-parse', '--show-toplevel'], { cwd }) | ||
| /** | ||
| * Absolute .git directory, similar to top-level | ||
| * | ||
| * @example "/Users/iiro/Documents/git/lint-staged/.git" | ||
| */ | ||
| const absoluteGitDirPromise = execGit(['rev-parse', '--absolute-git-dir'], { cwd }) | ||
| const [topLevel, absoluteGitDir] = await Promise.all([topLevelPromise, absoluteGitDirPromise]) | ||
| return path.relative(topLevel, absoluteGitDir) | ||
| } | ||
| /** Resolve git directory and possible submodule paths */ | ||
| export const resolveGitRepo = async (cwd = process.cwd()) => { | ||
@@ -22,13 +54,15 @@ try { | ||
| const gitDir = normalizePath(await execGit(['rev-parse', '--show-toplevel'], { cwd })) | ||
| debugLog('Resolved git directory to be `%s`', gitDir) | ||
| const relativeTopLevelDir = await execGit(['rev-parse', '--show-cdup'], { cwd }) | ||
| const topLevelDir = normalizePath(path.join(cwd, relativeTopLevelDir)) | ||
| debugLog('Resolved git repository top-level directory to be `%s`', topLevelDir) | ||
| const gitConfigDir = normalizePath(await execGit(['rev-parse', '--absolute-git-dir'], { cwd })) | ||
| const relativeGitConfigDir = await resolveRelativeGitDir(cwd) | ||
| const gitConfigDir = normalizePath(path.join(topLevelDir, relativeGitConfigDir)) | ||
| debugLog('Resolved git config directory to be `%s`', gitConfigDir) | ||
| return { gitDir, gitConfigDir } | ||
| return { topLevelDir, gitConfigDir } | ||
| } catch (error) { | ||
| debugLog('Failed to resolve git repo with error:', error) | ||
| return { error, gitDir: null, gitConfigDir: null } | ||
| return { error, topLevelDir: null, gitConfigDir: null } | ||
| } | ||
| } |
@@ -128,3 +128,3 @@ import chalk from 'chalk' | ||
| * @param {string} [options.cwd] | ||
| * @param {String} options.gitDir - Current git repo path | ||
| * @param {String} options.topLevelDir - Current git repo top-level path | ||
| * @param {Boolean} options.isFn - Whether the linter task is a function | ||
@@ -140,3 +140,3 @@ * @param {Array<string>} options.files — Filepaths to run the linter task against | ||
| files, | ||
| gitDir, | ||
| topLevelDir, | ||
| isFn, | ||
@@ -151,5 +151,5 @@ shell = false, | ||
| const execaOptions = { | ||
| // Only use gitDir as CWD if we are using the git binary | ||
| // Only use topLevelDir as CWD if we are using the git binary | ||
| // e.g `npm` should run tasks in the actual CWD | ||
| cwd: /^git(\.exe)?/i.test(cmd) ? gitDir : cwd, | ||
| cwd: /^git(\.exe)?/i.test(cmd) ? topLevelDir : cwd, | ||
| preferLocal: true, | ||
@@ -156,0 +156,0 @@ reject: false, |
+9
-9
@@ -97,4 +97,4 @@ /** @typedef {import('./index').Logger} Logger */ | ||
| const { gitDir, gitConfigDir } = await resolveGitRepo(cwd) | ||
| if (!gitDir) { | ||
| const { topLevelDir, gitConfigDir } = await resolveGitRepo(cwd) | ||
| if (!topLevelDir) { | ||
| if (!quiet) ctx.output.push(NOT_GIT_REPO) | ||
@@ -107,3 +107,3 @@ ctx.errors.add(GitRepoError) | ||
| // Stashing must be disabled with no initial commit. | ||
| const hasInitialCommit = await execGit(['log', '-1'], { cwd: gitDir }) | ||
| const hasInitialCommit = await execGit(['log', '-1'], { cwd: topLevelDir }) | ||
| .then(() => true) | ||
@@ -124,3 +124,3 @@ .catch(() => false) | ||
| const files = await getStagedFiles({ cwd: gitDir, diff, diffFilter }) | ||
| const files = await getStagedFiles({ cwd: topLevelDir, diff, diffFilter }) | ||
| if (!files) { | ||
@@ -139,3 +139,3 @@ if (!quiet) ctx.output.push(FAILED_GET_STAGED_FILES) | ||
| const foundConfigs = await searchConfigs({ configObject, configPath, cwd, gitDir }, logger) | ||
| const foundConfigs = await searchConfigs({ configObject, configPath, cwd, topLevelDir }, logger) | ||
| const numberOfConfigs = Object.keys(foundConfigs).length | ||
@@ -176,3 +176,3 @@ | ||
| const stagedFileChunks = chunkFiles({ baseDir: gitDir, files, maxArgLength, relative }) | ||
| const stagedFileChunks = chunkFiles({ baseDir: topLevelDir, files, maxArgLength, relative }) | ||
@@ -196,3 +196,3 @@ // Use actual cwd if it's specified, or there's only a single config file. | ||
| files: task.fileList, | ||
| gitDir, | ||
| topLevelDir, | ||
| shell, | ||
@@ -271,3 +271,3 @@ verbose, | ||
| const matchedFileChunks = chunkFiles({ | ||
| // matched files are relative to `cwd`, not `gitDir`, when `relative` is used | ||
| // matched files are relative to `cwd`, not `topLevelDir`, when `relative` is used | ||
| baseDir: cwd, | ||
@@ -282,3 +282,3 @@ files: Array.from(matchedFiles), | ||
| gitConfigDir, | ||
| gitDir, | ||
| topLevelDir, | ||
| matchedFileChunks, | ||
@@ -285,0 +285,0 @@ diff, |
@@ -38,3 +38,3 @@ /** @typedef {import('./index').Logger} Logger */ | ||
| export const searchConfigs = async ( | ||
| { configObject, configPath, cwd = process.cwd(), gitDir = cwd }, | ||
| { configObject, configPath, cwd = process.cwd(), topLevelDir = cwd }, | ||
| logger | ||
@@ -63,5 +63,7 @@ ) => { | ||
| /** Get all possible config files known to git */ | ||
| execGit(EXEC_GIT, { cwd: gitDir }).then(parseGitZOutput), | ||
| execGit(EXEC_GIT, { cwd: topLevelDir }).then(parseGitZOutput), | ||
| /** Get all possible config files from uncommitted files */ | ||
| execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: gitDir }).then(parseGitZOutput), | ||
| execGit([...EXEC_GIT, '--others', '--exclude-standard'], { cwd: topLevelDir }).then( | ||
| parseGitZOutput | ||
| ), | ||
| ]) | ||
@@ -79,3 +81,3 @@ | ||
| .filter(filterPossibleConfigFiles) | ||
| .map((file) => normalizePath(path.join(gitDir, file))) | ||
| .map((file) => normalizePath(path.join(topLevelDir, file))) | ||
| .filter(isInsideDirectory(cwd)) | ||
@@ -82,0 +84,0 @@ .sort(sortDeepestParth) |
@@ -29,2 +29,3 @@ import { constants } from 'node:fs' | ||
| } catch (error) { | ||
| debugLog('Failed to validate options: %o', options) | ||
| logger.error(invalidOption('cwd', options.cwd, error.message)) | ||
@@ -40,2 +41,3 @@ throw InvalidOptionsError | ||
| } catch (error) { | ||
| debugLog('Failed to validate options: %o', options) | ||
| logger.error(invalidOption('shell', options.shell, error.message)) | ||
@@ -46,3 +48,3 @@ throw InvalidOptionsError | ||
| debugLog('Validated options!') | ||
| debugLog('Validated options: %o', options) | ||
| } |
+10
-10
| { | ||
| "name": "lint-staged", | ||
| "version": "15.2.7", | ||
| "version": "15.2.8", | ||
| "description": "Lint files staged by git", | ||
| "license": "MIT", | ||
| "repository": "https://github.com/okonet/lint-staged", | ||
| "repository": "https://github.com/lint-staged/lint-staged", | ||
| "author": "Andrey Okonetchnikov <andrey@okonet.ru>", | ||
@@ -41,14 +41,14 @@ "maintainers": [ | ||
| "commander": "~12.1.0", | ||
| "debug": "~4.3.4", | ||
| "debug": "~4.3.6", | ||
| "execa": "~8.0.1", | ||
| "lilconfig": "~3.1.1", | ||
| "listr2": "~8.2.1", | ||
| "lilconfig": "~3.1.2", | ||
| "listr2": "~8.2.4", | ||
| "micromatch": "~4.0.7", | ||
| "pidtree": "~0.6.0", | ||
| "string-argv": "~0.3.2", | ||
| "yaml": "~2.4.2" | ||
| "yaml": "~2.5.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@changesets/changelog-github": "0.5.0", | ||
| "@changesets/cli": "2.27.3", | ||
| "@changesets/cli": "2.27.7", | ||
| "@commitlint/cli": "19.3.0", | ||
@@ -62,8 +62,8 @@ "@commitlint/config-conventional": "19.2.2", | ||
| "eslint-plugin-node": "11.1.0", | ||
| "eslint-plugin-prettier": "5.1.3", | ||
| "husky": "9.0.11", | ||
| "eslint-plugin-prettier": "5.2.1", | ||
| "husky": "9.1.4", | ||
| "jest": "29.7.0", | ||
| "jest-snapshot-serializer-ansi": "2.1.0", | ||
| "mock-stdin": "1.0.0", | ||
| "prettier": "3.2.5" | ||
| "prettier": "3.3.3" | ||
| }, | ||
@@ -70,0 +70,0 @@ "keywords": [ |
+9
-17
@@ -1,3 +0,7 @@ | ||
| # 🚫💩 lint-staged [](https://github.com/okonet/lint-staged/actions/workflows/push.yml) [](https://github.com/okonet/lint-staged/actions/workflows/tag.yml) [](https://badge.fury.io/js/lint-staged) [](https://codecov.io/gh/okonet/lint-staged) | ||
| # 🚫💩 lint-staged | ||
| [](https://badge.fury.io/js/lint-staged) | ||
| --- | ||
| Run linters against staged git files and don't let :poop: slip into your code base! | ||
@@ -239,13 +243,5 @@ | ||
| { | ||
| "*.css": [ | ||
| "stylelint --fix", | ||
| "prettier --write" | ||
| ], | ||
| "*.{js,jsx}": [ | ||
| "eslint --fix", | ||
| "prettier --write" | ||
| ], | ||
| "!(*.css|*.js|*.jsx)": [ | ||
| "prettier --write" | ||
| ] | ||
| "*.css": ["stylelint --fix", "prettier --write"], | ||
| "*.{js,jsx}": ["eslint --fix", "prettier --write"], | ||
| "!(*.css|*.js|*.jsx)": ["prettier --write"] | ||
| } | ||
@@ -493,4 +489,3 @@ ``` | ||
| ```shell | ||
| #!/usr/bin/env sh | ||
| . "$(dirname "$0")/_/husky.sh" | ||
| # .husky/pre-commit | ||
@@ -701,5 +696,2 @@ npx lint-staged | ||
| #!/usr/bin/env sh | ||
| . "$(dirname -- "$0")/_/husky.sh" | ||
| if sh -c ": >/dev/tty" >/dev/null 2>/dev/null; then exec >/dev/tty 2>&1; fi | ||
@@ -706,0 +698,0 @@ |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
120663
0.85%2085
1.71%13
-7.14%970
-0.82%+ Added
- Removed
Updated
Updated
Updated
Updated