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
4 changes: 2 additions & 2 deletions .github/workflows/autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ jobs:
if: ${{ github.event_name == 'push' || !github.event.pull_request.draft }}
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.1
- name: Setup Tools
uses: tanstack/config/.github/setup@main
- name: Fix formatting
run: pnpm prettier:write
run: pnpm format
- name: Generate labeler config
run: pnpm labeler-generate
- name: Apply fixes
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
name: Pull Request Labeler
name: Labeler

on:
- pull_request_target

permissions:
contents: read
pull-requests: write

jobs:
labeler:
runs-on: ubuntu-latest
Expand Down
9 changes: 5 additions & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: pr
name: PR

on:
pull_request:
Expand All @@ -13,6 +13,7 @@ env:

permissions:
contents: read
pull-requests: write

jobs:
test:
Expand All @@ -22,15 +23,15 @@ jobs:
TSR_TMP_DIR: ./tmp
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Start Nx Agents
run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"
- name: Setup Tools
uses: tanstack/config/.github/setup@main
- name: Get base and head commits for `nx affected`
uses: nrwl/nx-set-shas@v4.1.2
uses: nrwl/nx-set-shas@v4.4.0
with:
main-branch-name: main
- name: Run Checks
Expand All @@ -43,7 +44,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Setup Tools
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/ci.yml → .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: ci
name: Release

on:
workflow_dispatch:
Expand All @@ -20,15 +20,16 @@ env:
permissions:
contents: write
id-token: write
pull-requests: write

jobs:
test:
name: Build & Test
name: Release
if: github.repository_owner == 'TanStack'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.1
with:
fetch-depth: 0
- name: Start Nx Agents
Expand All @@ -44,9 +45,7 @@ jobs:
run: |
git config --global user.name 'Tanner Linsley'
git config --global user.email 'tannerlinsley@users.noreply.github.com'
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
pnpm run cipublish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
TAG: ${{ inputs.tag }}
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ TanStack Router is a type-safe router with built-in caching and URL state manage
- **Critical**: Always run unit and type tests during development - do not proceed if they fail
- **Test types:** `pnpm test:unit`, `pnpm test:types`, `pnpm test:eslint`, `pnpm test:e2e`, `pnpm test:build`
- **Full CI suite:** `pnpm test:ci`
- **Fix formatting:** `pnpm prettier:write`
- **Fix formatting:** `pnpm format`
- **Efficient targeted testing workflow:**
1. **Affected only:** `npx nx affected --target=test:unit` (compares to main branch)
2. **Specific packages:** `npx nx run @tanstack/react-router:test:unit`
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"test:pr": "nx affected --targets=test:eslint,test:unit,test:e2e,test:types,test:build,build",
"test:ci": "nx run-many --targets=test:eslint,test:unit,test:e2e,test:types,test:build,build",
"test:eslint": "nx affected --target=test:eslint --exclude=examples/**,e2e/**",
"test:format": "pnpm run prettier --check",
"test:unit": "nx affected --target=test:unit --exclude=examples/**,e2e/**",
"test:unit:dev": "pnpm run test:unit && nx watch --all -- pnpm run test:unit",
"test:build": "nx affected --target=test:build --exclude=examples/**",
Expand All @@ -25,12 +24,11 @@
"build:all": "nx run-many --target=build --exclude=examples/** --exclude=e2e/**",
"watch": "pnpm run build:all && nx watch --all -- pnpm run build:all",
"dev": "pnpm run watch",
"prettier": "prettier --experimental-cli --ignore-unknown '**/*'",
"prettier:write": "pnpm run prettier --write",
"format": "prettier --experimental-cli --ignore-unknown '**/*' --write",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

prettier npm script glob pattern Windows shell '**/*' vs .

💡 Result:

Short answer: use prettier --write . (recommended) or quote your glob in package.json so Prettier (not the shell) sees it. On Unix shells */ is expanded by the shell; on Windows (cmd.exe) the shell does not expand globs, so behaviour differs unless you let Prettier handle the pattern. See Prettier docs: quote globs for cross‑platform use and/or use . to format the current directory. [1][2][3]

Examples:

  • Recommended simple cross‑platform: "format": "prettier . --write". [1]
  • If you want a glob: "format": "prettier "**/*.{js,ts,css,json,md}" --write" (quotes ensure Prettier receives the glob instead of the shell expanding it). [1][2]

Sources:

  • Prettier CLI (quotes, '.' vs globs, glob resolution). [1]
  • npm scripts / shell differences (npm runs cmd.exe on Windows vs /bin/sh on Unix). [2]
  • glob behavior / globbing libraries used by tools. [3]

🌐 Web query:

npm scripts shell expansion glob patterns Windows cross-platform

💡 Result:

Short answer

  • Shell globbing (e.g. src/**/*.js) is performed by the shell, not by npm. POSIX shells expand globs before npm sees them; Windows cmd.exe does not, so relying on shell expansion breaks on Windows. [1][6][3]
  • Options: (A) let the CLI/tool do globbing (pass quoted pattern) or use a Node glob library; (B) use a cross-platform tool that implements its own globbing (rimraf, fast-glob/glob, etc.); (C) run a POSIX shell on Windows or change npm’s script-shell. [1][5][4][6]

What to do (concise recipes)

  • Prefer letting the tool handle the pattern (safe cross-platform):
    • Example: "lint": "eslint "src/**/*.ts"" — quote the pattern so eslint (which implements globbing) receives it. [1]
  • Use a Node-based glob library or small script when you need custom file lists:
    • Example: "lint": "node scripts/lint.js" and in scripts/lint.js use fast-glob or glob to resolve files. [5]
  • Use cross-platform CLI packages for OS-mismatched commands:
    • rm -> rimraf (supports --glob and runs the glob itself). [4]
    • set env vars -> cross-env / cross-env-shell. [2]
  • If you must rely on a shell that expands globs, change npm’s shell on Windows:
    • Set script-shell in .npmrc or install/run Git Bash / WSL / PowerShell and configure accordingly (npm uses cmd.exe by default on Windows). [6][2]

Notes / pitfalls

  • Quoting matters: don't let cmd interpret unquoted globs. Use double quotes in package.json so node tools receive the literal pattern. [1]
  • Some useful packages: glob / fast-glob for programmatic expansion; rimraf for safe deletions; cross-env for env vars. [5][4][2]
  • For complex cross-platform inline scripts, prefer a small Node script (most robust).

Sources

  • Cross-platform npm-scripts and quoting/globbing guidance. [1]
  • cross-env README (env handling, cross-env-shell note). [2]
  • StackOverflow examples and discussion about shell expansion on Windows. [3]
  • rimraf CLI (glob handling, cross-platform delete). [4]
  • glob npm package (programmatic globbing). [5]
  • npm scripts run-shell behavior / script-shell discussion. [6]

Consider a more portable target for the format script

The Prettier v3 --experimental-cli usage is fine, but shell glob expansion behaves differently across platforms: POSIX shells expand **/* before Prettier sees it, while Windows cmd.exe does not. Using . (current directory) is simpler and explicitly recommended by Prettier for cross-platform compatibility:

-    "format": "prettier --experimental-cli --ignore-unknown '**/*' --write",
+    "format": "prettier --experimental-cli --ignore-unknown . --write",
📝 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
"format": "prettier --experimental-cli --ignore-unknown '**/*' --write",
"format": "prettier --experimental-cli --ignore-unknown . --write",
🤖 Prompt for AI Agents
In package.json around line 27, the format script uses a shell glob '**/*' which
is expanded differently on POSIX vs Windows; change the script to use the
portable current-directory target '.' so Prettier runs consistently across
platforms (e.g. update the script to use "prettier --experimental-cli
--ignore-unknown . --write"), keeping the existing flags intact.

"cipublish": "node scripts/publish.js",
"gpt-generate": "node gpt/generate.js",
"set-ts-version": "node scripts/set-ts-version.js",
"labeler-generate": "node scripts/generate-labeler-config.mjs",
"labeler-generate": "node scripts/generate-labeler-config.ts",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n package.json | head -50

Repository: TanStack/router

Length of output: 2694


🏁 Script executed:

rg "tsx|ts-node" package.json

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cat -n scripts/generate-labeler-config.ts | head -20

Repository: TanStack/router

Length of output: 881


🏁 Script executed:

rg "type.*module" package.json

Repository: TanStack/router

Length of output: 78


🏁 Script executed:

cat package.json | grep -A 100 '"devDependencies"' | grep -E '"(tsx|ts-node|ts-loader|tsx-loader)"'

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

rg "tsx|ts-node|@ts" package.json

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

cat package.json | grep -A 200 '"devDependencies"' | head -120

Repository: TanStack/router

Length of output: 3411


🏁 Script executed:

rg "labeler-generate|verify-links" --type json --type yaml --type md

Repository: TanStack/router

Length of output: 199


🏁 Script executed:

git log --oneline scripts/generate-labeler-config.ts | head -5

Repository: TanStack/router

Length of output: 103


🏁 Script executed:

ls -la scripts/

Repository: TanStack/router

Length of output: 589


labeler-generate and test:docs scripts point Node directly at .ts files, which will fail without a TypeScript loader

With "type": "module" configured and no TS runtime like tsx or ts-node in devDependencies, node scripts/generate-labeler-config.ts and node scripts/verify-links.ts will fail at runtime. Node doesn't understand TypeScript syntax or .ts imports natively. Resolve by either:

  • Installing and using a TS loader (e.g., tsx or ts-node) in the script command, or
  • Compiling these scripts to JavaScript and pointing to the compiled output.
🤖 Prompt for AI Agents
In package.json around line 31 the "labeler-generate" (and similarly
"test:docs") script invokes Node directly on a .ts file which will fail at
runtime because Node cannot execute TypeScript files without a loader; fix by
either adding a TypeScript runner (e.g., install a devDependency like tsx or
ts-node and update the script to use it, e.g., "tsx
scripts/generate-labeler-config.ts") or compile the script to JS and point the
script to the compiled .js output; ensure the chosen tool is added to
devDependencies and update package.json scripts accordingly.

"cleanup-empty-packages": "node scripts/cleanup-empty-packages.mjs",
"test:docs": "node scripts/verify-links.ts"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@ import * as fs from 'node:fs'
import * as path from 'node:path'
import * as prettier from 'prettier'

/**
* Pairs of package labels and their corresponding paths
* @typedef {[string, string]} LabelerPair
*/

/**
* @returns {Array<LabelerPair>} Pairs of package labels and their corresponding paths
*/
function readPairsFromFs() {
/** Pairs of package labels and their corresponding paths */
type LabelerPair = [string, string]

function readPairsFromFs(): Array<LabelerPair> {
const ignored = new Set(['.DS_Store'])

/** @type {Array<LabelerPair>} */
const pairs = []
const pairs: Array<LabelerPair> = []

// Add subfolders in the packages folder, i.e. packages/**
fs.readdirSync(path.resolve('packages'))
Expand All @@ -40,11 +34,7 @@ function readPairsFromFs() {
return pairs
}

/**
* @param {Array<LabelerPair>} pairs
* @returns {Promise<string>} YAML string for the labeler config
*/
async function generateLabelerYaml(pairs) {
async function generateLabelerYaml(pairs: Array<LabelerPair>): Promise<string> {
function s(n = 1) {
return ' '.repeat(n)
}
Expand Down Expand Up @@ -96,7 +86,7 @@ async function run() {
// Convert the pairs into valid yaml
const yamlStr = await generateLabelerYaml(pairs)

// Write to '.github/labeler.yml'
// Write to 'labeler-config.yml'
const configPath = path.resolve('labeler-config.yml')
fs.writeFileSync(configPath, yamlStr, {
encoding: 'utf-8',
Expand Down
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"module": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"noEmit": true,
"noImplicitReturns": true,
Expand All @@ -24,5 +24,5 @@
"stripInternal": true,
"target": "ES2020"
},
"include": ["eslint.config.js", "prettier.config.js", "scripts"]
"include": ["*.config.*", "scripts"]
}
Loading