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/workspace-root-intent-warning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/intent': patch
---

Recognize Intent installed as a devDependency at the owning workspace root when validating package skills.
17 changes: 16 additions & 1 deletion packages/intent/src/commands/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,31 @@ function collectPackagingWarnings(context: ProjectContext): Array<string> {
if (!existsSync(pkgJsonPath)) return []

let pkgJson: Record<string, unknown>
let devDeps: Record<string, string> | undefined
try {
pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
devDeps = pkgJson.devDependencies as Record<string, string> | undefined
if (
!devDeps?.['@tanstack/intent'] &&
context.workspaceRoot &&
context.workspaceRoot !== context.packageRoot
) {
const workspaceManifestPath = join(context.workspaceRoot, 'package.json')
if (existsSync(workspaceManifestPath)) {
const workspaceManifest = JSON.parse(
readFileSync(workspaceManifestPath, 'utf8'),
) as Record<string, unknown>
devDeps = workspaceManifest.devDependencies as
Record<string, string> | undefined
}
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
return [`Could not parse package.json: ${msg}`]
}

const warnings: Array<string> = []

const devDeps = pkgJson.devDependencies as Record<string, string> | undefined
if (!devDeps?.['@tanstack/intent']) {
warnings.push('@tanstack/intent is not in devDependencies')
}
Expand Down
56 changes: 56 additions & 0 deletions packages/intent/tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3197,6 +3197,62 @@ describe('cli commands', () => {
expect(output).toContain('Framework skills must have a "requires" field')
})

it.each(['package.json', 'pnpm-workspace.yaml'])(
'recognizes Intent at the workspace root declared by %s',
async (workspaceFile) => {
const root = mkdtempSync(
join(realTmpdir, 'intent-cli-validate-root-dep-'),
)
tempDirs.push(root)
const manifest = {
private: true,
...(workspaceFile === 'package.json'
? { workspaces: ['packages/*'] }
: {}),
devDependencies: { '@tanstack/intent': '^0.4.0' },
}
writeJson(join(root, 'package.json'), manifest)
if (workspaceFile === 'pnpm-workspace.yaml')
writeFileSync(
join(root, 'pnpm-workspace.yaml'),
'packages:\n - packages/*\n',
)
const packageDir = join(root, 'packages', 'client')
writeJson(join(packageDir, 'package.json'), {
name: 'client',
keywords: ['tanstack-intent'],
files: ['skills'],
})
writeSkillMd(join(packageDir, 'skills', 'query'), {
name: 'query',
description: 'Query the client.',
})

for (const cwd of [root, packageDir]) {
process.chdir(cwd)
logSpy.mockClear()
expect(await main(['validate'])).toBe(0)
expect(logSpy.mock.calls.flat().join('\n')).not.toContain(
'@tanstack/intent is not in devDependencies',
)
}

writeJson(join(root, 'package.json'), {
...manifest,
devDependencies: {},
})
writeJson(join(root, 'packages', 'tooling', 'package.json'), {
name: 'tooling',
devDependencies: { '@tanstack/intent': '^0.4.0' },
})
logSpy.mockClear()
expect(await main(['validate'])).toBe(0)
expect(logSpy.mock.calls.flat().join('\n')).toContain(
'@tanstack/intent is not in devDependencies',
)
},
)

it('validates package skills from repo root without root packaging warnings', async () => {
const root = mkdtempSync(join(realTmpdir, 'intent-cli-validate-mono-'))
tempDirs.push(root)
Expand Down
Loading