Skip to content

Commit 000b4f3

Browse files
chore: support vibe-rules in react-router (#4263)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent fdf31ff commit 000b4f3

File tree

5 files changed

+214
-3
lines changed

5 files changed

+214
-3
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,6 @@ app.config.ts.timestamp_*
7676

7777
# Handling VSCode settings
7878
/.vscode/
79-
!/examples/react/**/.vscode/settings.json
79+
!/examples/react/**/.vscode/settings.json
80+
81+
**/llms

docs/router/framework/react/installation.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,19 @@ TypeScript is _optional_, but **HIGHLY** recommended! If you are using it, pleas
3333

3434
> [!IMPORTANT]
3535
> We aim to support the last five minor versions of TypeScript. If you are using an older version, you may run into issues. Please upgrade to the latest version of TypeScript to ensure compatibility. We may drop support for older versions of TypeScript, outside of the range mentioned above, without warning in a minor or patch release.
36+
37+
### Vibe Coding Support
38+
39+
All of our documentation for TanStack React Router is integrated into the NPM module and can be easily installed as Vibe coding rules. You can integrate Vibe coding rules into the editor of your choice using [vibe-rules](https://www.npmjs.com/package/vibe-rules).
40+
41+
```bash
42+
pnpm add -g vibe-rules
43+
```
44+
45+
Then run `vibe-rules` with the editor of your choice. Here is an example for Cursor:
46+
47+
```bash
48+
vibe-rules install cursor
49+
```
50+
51+
But you can also use `windsurf`, `claude-code`, etc. Check the [vibe-rules](https://www.npmjs.com/package/vibe-rules) documentation for more information.

packages/react-router/package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"test:perf": "vitest bench",
3939
"test:perf:dev": "pnpm run test:perf --watch --hideSkippedTests",
4040
"test:build": "publint --strict && attw --ignore-rules no-resolution --pack .",
41-
"build": "vite build"
41+
"build": "pnpm run build:lib && pnpm run build:llm",
42+
"build:lib": "vite build",
43+
"build:llm": "node ../../scripts/llms-generate.mjs react-router && tsc -p ./llms/tsconfig.json"
4244
},
4345
"type": "module",
4446
"types": "dist/esm/index.d.ts",
@@ -55,7 +57,13 @@
5557
"default": "./dist/cjs/index.cjs"
5658
}
5759
},
58-
"./package.json": "./package.json"
60+
"./package.json": "./package.json",
61+
"./llms": {
62+
"import": {
63+
"types": "./dist/llms/index.d.ts",
64+
"default": "./dist/llms/index.js"
65+
}
66+
}
5967
},
6068
"sideEffects": false,
6169
"files": [
@@ -81,6 +89,7 @@
8189
"combinate": "^1.1.11",
8290
"react": "^19.0.0",
8391
"react-dom": "^19.0.0",
92+
"vibe-rules": "^0.2.55",
8493
"zod": "^3.24.2"
8594
},
8695
"peerDependencies": {

pnpm-lock.yaml

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/llms-generate.mjs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env node
2+
import fs from 'node:fs'
3+
import path from 'node:path'
4+
5+
const DOCS_DIR = '../../docs'
6+
const LLMS_DIR = './llms'
7+
const RULES_DIR = './llms/rules'
8+
9+
const packages = {
10+
'react-router': [
11+
{
12+
paths: [`${DOCS_DIR}/router/framework/react/api/router`],
13+
description: 'TanStack Router: API',
14+
name: 'api',
15+
globs: ['src/**/*.ts', 'src/**/*.tsx'],
16+
},
17+
{
18+
paths: [`${DOCS_DIR}/router/framework/react/guide`],
19+
description: 'TanStack Router: Guide',
20+
name: 'guide',
21+
globs: ['src/**/*.ts', 'src/**/*.tsx'],
22+
},
23+
{
24+
paths: [`${DOCS_DIR}/router/framework/react/routing`],
25+
description: 'TanStack Router: Routing',
26+
name: 'routing',
27+
globs: ['src/**/*.ts', 'src/**/*.tsx'],
28+
},
29+
{
30+
paths: [
31+
`${DOCS_DIR}/router/framework/react/overview.md`,
32+
`${DOCS_DIR}/router/framework/react/quick-start.md`,
33+
`${DOCS_DIR}/router/framework/react/devtools.md`,
34+
`${DOCS_DIR}/router/framework/react/migrate-from-react-router.md`,
35+
`${DOCS_DIR}/router/framework/react/migrate-from-react-location.md`,
36+
`${DOCS_DIR}/router/framework/react/faq.md`,
37+
],
38+
description: 'TanStack Router: Setup and Architecture',
39+
name: 'setup-and-architecture',
40+
globs: [
41+
'package.json',
42+
'vite.config.ts',
43+
'tsconfig.json',
44+
'src/**/*.ts',
45+
'src/**/*.tsx',
46+
],
47+
},
48+
],
49+
}
50+
51+
const pkg = process.argv[2]
52+
if (!pkg) {
53+
console.error('Usage: node scripts/llms-generate.mjs <package-name>')
54+
process.exit(1)
55+
}
56+
if (!packages[pkg]) {
57+
console.error(`Package '${pkg}' not found`)
58+
process.exit(1)
59+
}
60+
61+
function camelCase(str) {
62+
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
63+
}
64+
65+
function extractFrontMatter(content) {
66+
const frontMatterEndIndex = content.indexOf('---', 3) + 3
67+
const frontMatter = content.slice(0, frontMatterEndIndex)
68+
const bodyContent = content.slice(frontMatterEndIndex).trim()
69+
return { frontMatter, bodyContent }
70+
}
71+
72+
function convertMarkdownToTypeScript(markdownContent) {
73+
const sanitizedContent = markdownContent
74+
.replace(/`/g, '\\`')
75+
.replace(/\$\{/g, '\\${')
76+
return `const content = \`${sanitizedContent}\`;\n\nexport default content;\n`
77+
}
78+
79+
function mergeFiles(files, outputFile) {
80+
let mergedContent = ''
81+
for (const file of files) {
82+
const content = fs.readFileSync(file, 'utf-8')
83+
const { frontMatter, bodyContent } = extractFrontMatter(content)
84+
const title = frontMatter.match(/title:\s*(.+)/)[1].trim()
85+
mergedContent += `# ${title}\n\n${bodyContent}\n\n`
86+
}
87+
fs.writeFileSync(
88+
outputFile,
89+
convertMarkdownToTypeScript(mergedContent),
90+
'utf-8',
91+
)
92+
}
93+
94+
if (!fs.existsSync(RULES_DIR)) {
95+
fs.mkdirSync(RULES_DIR, { recursive: true })
96+
}
97+
98+
// Create the rules files
99+
const imports = []
100+
const rules = []
101+
for (const { paths, name, description, globs } of packages[pkg]) {
102+
const files = []
103+
for (const p of paths) {
104+
if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
105+
files.push(
106+
...fs
107+
.readdirSync(p)
108+
.filter((file) => file.endsWith('.md'))
109+
.map((file) => path.join(p, file)),
110+
)
111+
} else {
112+
files.push(p)
113+
}
114+
}
115+
mergeFiles(files.flat(), path.join(RULES_DIR, `${name}.ts`))
116+
imports.push(`import ${camelCase(name)} from './rules/${name}.js'`)
117+
rules.push(`{
118+
name: '${name}',
119+
description: '${description}',
120+
rule: ${camelCase(name)},
121+
alwaysApply: false,
122+
globs: [${globs.map((glob) => `'${glob}'`).join(', ')}],
123+
}`)
124+
}
125+
126+
// Create the index.ts file
127+
const indexFile = path.join(LLMS_DIR, 'index.ts')
128+
const indexContent = `${imports.join('\n')}
129+
130+
import type { PackageRuleItem } from 'vibe-rules'
131+
132+
const rules: Array<PackageRuleItem> = [
133+
${rules.join(',\n')}
134+
]
135+
136+
export default rules
137+
`
138+
fs.writeFileSync(indexFile, indexContent, 'utf-8')
139+
140+
fs.writeFileSync(
141+
path.join(LLMS_DIR, 'tsconfig.json'),
142+
JSON.stringify(
143+
{
144+
compilerOptions: {
145+
module: 'ESNext',
146+
moduleResolution: 'bundler',
147+
target: 'ESNext',
148+
lib: ['ESNext', 'DOM'],
149+
declaration: true,
150+
outDir: '../dist/llms',
151+
},
152+
include: ['./index.ts', './rules/*.ts'],
153+
exclude: ['node_modules', 'dist'],
154+
},
155+
null,
156+
2,
157+
),
158+
'utf-8',
159+
)

0 commit comments

Comments
 (0)