Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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

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