project-reference-faces.spec.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { afterEach, describe, expect, it } from 'vitest'
  5. import { collectProjectReferenceFaceViolations } from './project-reference-faces.ts'
  6. const roots: string[] = []
  7. afterEach(() => {
  8. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  9. })
  10. function writeJson(path: string, value: unknown): void {
  11. writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`)
  12. }
  13. function workspaceFixture(options: {
  14. readonly host: readonly string[]
  15. readonly client: readonly string[]
  16. }): string {
  17. const root = mkdtempSync(join(tmpdir(), 'dsh-project-reference-faces-'))
  18. roots.push(root)
  19. const shared = join(root, 'packages/core/shared')
  20. const split = join(root, 'packages/api/split')
  21. mkdirSync(shared, { recursive: true })
  22. mkdirSync(split, { recursive: true })
  23. writeJson(join(root, 'tsconfig.base.json'), {})
  24. writeJson(join(root, 'tsconfig.base.client.json'), { extends: './tsconfig.base.json' })
  25. writeJson(join(shared, 'package.json'), { name: '@deepseek-ai/dsh-shared' })
  26. writeJson(join(shared, 'tsconfig.json'), {
  27. extends: '../../../tsconfig.base.json',
  28. references: [],
  29. })
  30. writeJson(join(split, 'package.json'), { name: '@deepseek-ai/dsh-split' })
  31. writeJson(join(split, 'tsconfig.json'), {
  32. files: [],
  33. references: [{ path: './tsconfig.host.json' }, { path: './tsconfig.client.json' }],
  34. })
  35. writeJson(join(split, 'tsconfig.host.json'), { references: [{ path: '../../core/shared' }] })
  36. writeJson(join(split, 'tsconfig.client.json'), { references: [{ path: '../../core/shared' }] })
  37. writeJson(join(root, 'tsconfig.host.json'), {
  38. references: options.host.map(path => ({ path })),
  39. })
  40. writeJson(join(root, 'tsconfig.client.json'), {
  41. references: options.client.map(path => ({ path })),
  42. })
  43. return root
  44. }
  45. describe('Project Reference compiler faces', () => {
  46. it('allows neutral projects in either graph and matching split leaves', () => {
  47. const root = workspaceFixture({
  48. host: ['./packages/core/shared', './packages/api/split/tsconfig.host.json'],
  49. client: ['./packages/core/shared', './packages/api/split/tsconfig.client.json'],
  50. })
  51. expect(collectProjectReferenceFaceViolations(root)).toEqual([])
  52. })
  53. it('rejects the opposite leaf and the solution root of a split project', () => {
  54. const root = workspaceFixture({
  55. host: [
  56. './packages/api/split/tsconfig.host.json',
  57. './packages/api/split/tsconfig.client.json',
  58. ],
  59. client: ['./packages/api/split'],
  60. })
  61. expect(collectProjectReferenceFaceViolations(root)).toEqual([
  62. 'tsconfig.client.json: Project Reference "./packages/api/split" enters split project packages/api/split from a Client config; reference "packages/api/split/tsconfig.client.json" instead',
  63. 'tsconfig.host.json: Project Reference "./packages/api/split/tsconfig.client.json" enters split project packages/api/split from a Host config; reference "packages/api/split/tsconfig.host.json" instead',
  64. ])
  65. })
  66. it('uses the referencing project face throughout the reachable graph', () => {
  67. const root = workspaceFixture({
  68. host: ['./packages/core/host-consumer'],
  69. client: ['./packages/core/client-consumer'],
  70. })
  71. const hostConsumer = join(root, 'packages/core/host-consumer')
  72. mkdirSync(hostConsumer, { recursive: true })
  73. writeJson(join(hostConsumer, 'package.json'), { name: '@deepseek-ai/dsh-host-consumer' })
  74. writeJson(join(hostConsumer, 'tsconfig.json'), {
  75. extends: '../../../tsconfig.base.json',
  76. references: [{ path: '../../api/split/tsconfig.client.json' }],
  77. })
  78. const clientConsumer = join(root, 'packages/core/client-consumer')
  79. mkdirSync(clientConsumer, { recursive: true })
  80. writeJson(join(clientConsumer, 'package.json'), { name: '@deepseek-ai/dsh-client-consumer' })
  81. writeJson(join(clientConsumer, 'tsconfig.json'), {
  82. extends: '../../../tsconfig.base.client.json',
  83. references: [{ path: '../../api/split/tsconfig.host.json' }],
  84. })
  85. expect(collectProjectReferenceFaceViolations(root)).toEqual([
  86. 'packages/core/client-consumer/tsconfig.json: Project Reference "../../api/split/tsconfig.host.json" enters split project packages/api/split from a Client config; reference "packages/api/split/tsconfig.client.json" instead',
  87. 'packages/core/host-consumer/tsconfig.json: Project Reference "../../api/split/tsconfig.client.json" enters split project packages/api/split from a Host config; reference "packages/api/split/tsconfig.host.json" instead',
  88. ])
  89. })
  90. })