Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
npm run check
npx lint-staged
npm run prebuild
Comment on lines +1 to +2
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Current pre-commit hook:"
nl -ba .husky/pre-commit

echo
echo "Inspect husky shim execution mode (if present):"
if [ -d .husky/_ ]; then
  fd '^(h|pre-commit)$' .husky/_ --exec sh -c 'echo "---- $1 ----"; sed -n "1,140p" "$1"' sh {}
else
  echo ".husky/_ not found"
fi

echo
echo "Reference shell behavior:"
sh -c 'false; true'; echo "exit_without_-e=$?"
sh -ec 'false; true' || echo "exit_with_-e=non-zero (expected)"

Repository: PashaSchool/utils-kit

Length of output: 149


🏁 Script executed:

#!/bin/bash

echo "Current pre-commit hook:"
cat -n .husky/pre-commit

echo
echo "Inspect husky shim execution mode (if present):"
if [ -d .husky/_ ]; then
  echo "Contents of .husky/_ directory:"
  ls -la .husky/_
  echo
  echo "Checking shim files:"
  for f in .husky/_/*; do
    if [ -f "$f" ]; then
      echo "---- $(basename "$f") ----"
      head -20 "$f"
      echo
    fi
  done
else
  echo ".husky/_ directory not found"
fi

echo "Reference shell behavior:"
echo "Without set -e:"
sh -c 'false; true'; echo "exit code: $?"
echo
echo "With set -e:"
(sh -ec 'false; true' 2>&1) || echo "exit code: non-zero (expected)"

Repository: PashaSchool/utils-kit

Length of output: 314


🏁 Script executed:

#!/bin/bash

echo "=== Husky Configuration ==="
if [ -f .huskyrc ]; then
  echo "Found .huskyrc:"
  cat .huskyrc
fi

if [ -f .huskyrc.json ]; then
  echo "Found .huskyrc.json:"
  cat .huskyrc.json
fi

echo
echo "=== Husky in package.json ==="
if [ -f package.json ]; then
  cat package.json | grep -A 10 '"husky"' || echo "No husky config in package.json"
fi

echo
echo "=== Husky version ==="
if [ -f package.json ]; then
  grep -E '"husky":|"@husky' package.json
fi

if [ -f package-lock.json ]; then
  echo "From package-lock.json:"
  grep -A 2 '"husky":' package-lock.json | head -10
fi

echo
echo "=== All .husky files ==="
find .husky -type f -exec sh -c 'echo "--- {} ---"; head -10 "{}"' \;

Repository: PashaSchool/utils-kit

Length of output: 701


🌐 Web query:

Husky 9.x hook execution shell behavior default

💡 Result:

In Husky 9.x, hooks in .husky/ are executed by Git as normal hook files, not by a Husky-specific runner. The docs even show creating a hook as plain lines like npm test (no shebang) in .husky/pre-commit [1].

Default shell behavior: Husky’s documented expectation is POSIX sh-compatible hook scripting (“Use POSIX shell…”) [2]. Practically, that means write hooks as POSIX sh, unless you explicitly choose another interpreter.

If you want Bash (or something else): make the hook file executable and add a shebang like #!/usr/bin/env bash (or explicitly run bash -c ...) so your hook runs under Bash instead of POSIX sh. (Otherwise, Bash-only builtins like source may fail under sh.) [2]

Sources: [1] [2]


Chain commands to prevent prebuild running after lint-staged failure.

This hook runs under POSIX shell without set -e, so npm run prebuild executes even if npx lint-staged fails. Use explicit failure chaining to ensure the build skips when linting fails.

🔧 Proposed change
-npx lint-staged
-npm run prebuild
+npx lint-staged && npm run prebuild
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
npx lint-staged
npm run prebuild
npx lint-staged && npm run prebuild
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.husky/pre-commit around lines 1 - 2, The pre-commit hook runs both commands
unconditionally so `npm run prebuild` still runs if `npx lint-staged` fails;
update the .husky/pre-commit script to short-circuit on failure (e.g., use
explicit chaining such as running `npx lint-staged` && `npm run prebuild` or
enable `set -e` at the top) so that the prebuild step only executes when `npx
lint-staged` succeeds.

46 changes: 45 additions & 1 deletion apps/playground/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// import {useUrlParams, useBatchUrlParams} from 'react-url-query-params'
import { useBatchUrlParams, useUrlParams } from 'react-url-query-params';
import { type ExportController, ExportControllerSingleton } from 'export-csv-core';
import { useEffect, useRef, useState } from 'react';
import viteLogo from '/vite.svg';
Expand Down Expand Up @@ -50,6 +50,49 @@ function useMessageExportCSV(cb: (payload: Payload) => void) {
}, [cb]);
}

function UrlParamsDemo() {
const { view, setView, toggleView, clearView, isViewGrid, isViewTable } = useUrlParams({
keyName: 'view',
options: ['grid', 'table'] as const,
});

const { set, clearParams, isFilterActive, isFilterInactive, isSortAsc, isSortDesc } = useBatchUrlParams({
filter: ['active', 'inactive'] as const,
sort: ['asc', 'desc'] as const,
});

return (
<div style={{ border: '1px solid #444', borderRadius: 8, padding: '1rem', marginBottom: '1rem', textAlign: 'left' }}>
<h2>react-url-query-params demo</h2>

<section style={{ marginBottom: '1rem' }}>
<h3>useUrlParams — single param</h3>
<p>Current URL param <code>?view</code>: <strong>{view ?? 'null'}</strong></p>
<p>isViewGrid: <strong>{String(isViewGrid)}</strong> | isViewTable: <strong>{String(isViewTable)}</strong></p>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button onClick={() => setView('grid')}>setView('grid')</button>
<button onClick={() => setView('table')}>setView('table')</button>
<button onClick={() => toggleView()}>toggleView()</button>
<button onClick={() => clearView()}>clearView()</button>
<button onClick={() => setView('grid', { replace: true })}>setView('grid', replace)</button>
</div>
</section>

<section>
<h3>useBatchUrlParams — multiple params</h3>
<p>isFilterActive: <strong>{String(isFilterActive)}</strong> | isFilterInactive: <strong>{String(isFilterInactive)}</strong></p>
<p>isSortAsc: <strong>{String(isSortAsc)}</strong> | isSortDesc: <strong>{String(isSortDesc)}</strong></p>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button onClick={() => set({ filter: 'active', sort: 'asc' })}>set active + asc</button>
<button onClick={() => set({ filter: 'inactive', sort: 'desc' })}>set inactive + desc</button>
<button onClick={() => set({ filter: 'active' })}>set filter only</button>
<button onClick={() => clearParams()}>clearParams()</button>
</div>
</section>
</div>
);
}

function App() {
const [count, setCount] = useState(0);

Expand All @@ -61,6 +104,7 @@ function App() {

return (
<>
<UrlParamsDemo />
<div>
<button
type="button"
Expand Down
Loading