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
5 changes: 5 additions & 0 deletions .changeset/neat-pots-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/intent': patch
---

Make `scanForIntents` and `scanLibrary` synchronous instead of returning Promises for purely synchronous work. Clean up unnecessary async/await throughout source and tests, extract DRY test helpers, and improve type narrowing.
2 changes: 1 addition & 1 deletion packages/intent/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function scanIntentsOrFail(): Promise<ScanResult> {
const { scanForIntents } = await import('./scanner.js')

try {
return await scanForIntents()
return scanForIntents()
} catch (err) {
fail((err as Error).message)
}
Expand Down
6 changes: 3 additions & 3 deletions packages/intent/src/intent-library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import type { LibraryScanResult } from './library-scanner.js'
// Commands
// ---------------------------------------------------------------------------

async function cmdList(): Promise<void> {
function cmdList(): void {
let result: LibraryScanResult
try {
result = await scanLibrary(process.argv[1]!)
result = scanLibrary(process.argv[1]!)
} catch (err) {
console.error((err as Error).message)
process.exit(1)
Expand Down Expand Up @@ -90,7 +90,7 @@ const command = process.argv[2]
switch (command) {
case 'list':
case undefined:
await cmdList()
cmdList()
break
case 'install':
cmdInstall()
Expand Down
4 changes: 2 additions & 2 deletions packages/intent/src/library-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ function discoverSkills(skillsDir: string): Array<SkillEntry> {
// Main scanner
// ---------------------------------------------------------------------------

export async function scanLibrary(
export function scanLibrary(
scriptPath: string,
_projectRoot?: string,
): Promise<LibraryScanResult> {
): LibraryScanResult {
const packages: Array<LibraryPackage> = []
const warnings: Array<string> = []
const visited = new Set<string>()
Expand Down
2 changes: 1 addition & 1 deletion packages/intent/src/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ function toVersionConflict(
// Main scanner
// ---------------------------------------------------------------------------

export async function scanForIntents(root?: string): Promise<ScanResult> {
export function scanForIntents(root?: string): ScanResult {
const projectRoot = root ?? process.cwd()
const packageManager = detectPackageManager(projectRoot)
const nodeModulesDir = join(projectRoot, 'node_modules')
Expand Down
44 changes: 22 additions & 22 deletions packages/intent/tests/library-scanner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ afterEach(() => {
// ---------------------------------------------------------------------------

describe('scanLibrary', () => {
it('returns the home package with its skills', async () => {
it('returns the home package with its skills', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -67,7 +67,7 @@ describe('scanLibrary', () => {
description: 'File-based route definitions',
})

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

expect(result.warnings).toEqual([])
expect(result.packages).toHaveLength(1)
Expand All @@ -81,7 +81,7 @@ describe('scanLibrary', () => {
)
})

it('includes the full path to each SKILL.md', async () => {
it('includes the full path to each SKILL.md', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -91,13 +91,13 @@ describe('scanLibrary', () => {
const skillDir = createDir(pkgDir, 'skills', 'routing')
writeSkillMd(skillDir, { name: 'routing', description: 'Routing patterns' })

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const skill = result.packages[0]!.skills[0]!
expect(skill.path).toBe(join(pkgDir, 'skills', 'routing', 'SKILL.md'))
})

it('recursively discovers deps with tanstack-intent keyword', async () => {
it('recursively discovers deps with tanstack-intent keyword', () => {
// Home package: @tanstack/router, depends on @tanstack/query
const routerDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(routerDir, 'package.json'), {
Expand Down Expand Up @@ -127,7 +127,7 @@ describe('scanLibrary', () => {
description: 'Query and mutation patterns',
})

const result = await scanLibrary(scriptPath(routerDir), root)
const result = scanLibrary(scriptPath(routerDir), root)

expect(result.warnings).toEqual([])
expect(result.packages).toHaveLength(2)
Expand All @@ -141,7 +141,7 @@ describe('scanLibrary', () => {
expect(query.skills[0]!.description).toBe('Query and mutation patterns')
})

it('discovers deps via peerDependencies', async () => {
it('discovers deps via peerDependencies', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -159,13 +159,13 @@ describe('scanLibrary', () => {
const querySkill = createDir(queryDir, 'skills', 'fetching')
writeSkillMd(querySkill, { name: 'fetching', description: 'Fetching' })

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const names = result.packages.map((p) => p.name)
expect(names).toContain('@tanstack/query')
})

it('skips deps without tanstack-intent keyword or bin.intent', async () => {
it('skips deps without tanstack-intent keyword or bin.intent', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -183,13 +183,13 @@ describe('scanLibrary', () => {
const reactSkill = createDir(reactDir, 'skills', 'hooks')
writeSkillMd(reactSkill, { name: 'hooks', description: 'React hooks' })

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const names = result.packages.map((p) => p.name)
expect(names).not.toContain('react')
})

it('follows deps with legacy bin.intent (backwards compat)', async () => {
it('follows deps with legacy bin.intent (backwards compat)', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -210,13 +210,13 @@ describe('scanLibrary', () => {
const querySkill = createDir(queryDir, 'skills', 'fetching')
writeSkillMd(querySkill, { name: 'fetching', description: 'Fetching' })

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const names = result.packages.map((p) => p.name)
expect(names).toContain('@tanstack/query')
})

it('skips deps with other keywords but not tanstack-intent', async () => {
it('skips deps with other keywords but not tanstack-intent', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -234,13 +234,13 @@ describe('scanLibrary', () => {
const libSkill = createDir(libDir, 'skills', 'core')
writeSkillMd(libSkill, { name: 'core', description: 'Core patterns' })

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const names = result.packages.map((p) => p.name)
expect(names).not.toContain('some-lib')
})

it('handles packages with no skills/ directory', async () => {
it('handles packages with no skills/ directory', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -249,13 +249,13 @@ describe('scanLibrary', () => {
})
// No skills/ directory

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

expect(result.packages).toHaveLength(1)
expect(result.packages[0]!.skills).toEqual([])
})

it('does not visit the same package twice (cycle detection)', async () => {
it('does not visit the same package twice (cycle detection)', () => {
// router -> query -> router (circular)
const routerDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(routerDir, 'package.json'), {
Expand All @@ -273,15 +273,15 @@ describe('scanLibrary', () => {
dependencies: { '@tanstack/router': '^1.0.0' }, // circular back
})

const result = await scanLibrary(scriptPath(routerDir), root)
const result = scanLibrary(scriptPath(routerDir), root)

// Each package appears exactly once
const names = result.packages.map((p) => p.name)
expect(names).toHaveLength(2)
expect(new Set(names).size).toBe(2)
})

it('discovers sub-skills within a package', async () => {
it('discovers sub-skills within a package', () => {
const pkgDir = createDir(root, 'node_modules', '@tanstack', 'router')
writeJson(join(pkgDir, 'package.json'), {
name: '@tanstack/router',
Expand All @@ -299,7 +299,7 @@ describe('scanLibrary', () => {
description: 'Nested route patterns',
})

const result = await scanLibrary(scriptPath(pkgDir), root)
const result = scanLibrary(scriptPath(pkgDir), root)

const skills = result.packages[0]!.skills
expect(skills).toHaveLength(2)
Expand All @@ -308,10 +308,10 @@ describe('scanLibrary', () => {
expect(names).toContain('routing/nested-routes')
})

it('returns a warning when home package.json cannot be found', async () => {
it('returns a warning when home package.json cannot be found', () => {
const fakeScript = join(root, 'nowhere', 'bin', 'intent.js')

const result = await scanLibrary(fakeScript, root)
const result = scanLibrary(fakeScript, root)

expect(result.packages).toEqual([])
expect(result.warnings).toHaveLength(1)
Expand Down
Loading
Loading