gen-tsconfig-paths.spec.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { readFileSync } from 'node:fs'
  2. import { resolve } from 'node:path'
  3. import { describe, expect, it } from 'vitest'
  4. import {
  5. collectPackageAliases,
  6. collectPackageNames,
  7. mappedSpecifiers,
  8. renderAliases,
  9. uncoveredPackages,
  10. writeRegion,
  11. } from './gen-tsconfig-paths.ts'
  12. const root = resolve(import.meta.dirname, '..')
  13. describe('generated tsconfig package aliases', () => {
  14. it('maps each package to its own source directory', () => {
  15. const aliases = collectPackageAliases()
  16. expect(aliases.length).toBeGreaterThan(100)
  17. const session = aliases.find(alias => alias.specifier === '@deepseek-ai/dsh-session')
  18. expect(session).toEqual({
  19. specifier: '@deepseek-ai/dsh-session',
  20. source: './packages/core/session/src',
  21. })
  22. // Sorted, so a package added anywhere lands in a stable spot in the diff.
  23. expect([...aliases].sort((a, b) => a.specifier.localeCompare(b.specifier))).toEqual(aliases)
  24. // Only packages named after their directory: the rest carry hand-written
  25. // aliases, because the removed wildcards could never have resolved them.
  26. expect(aliases.some(alias => alias.specifier === '@deepseek-ai/dsh-typert-protocol')).toBe(false)
  27. })
  28. it('yields to a hand-written alias and closes without a trailing comma', () => {
  29. const aliases = [
  30. { specifier: '@deepseek-ai/dsh-a', source: './packages/g/a/src' },
  31. { specifier: '@deepseek-ai/dsh-b', source: './packages/g/b/src' },
  32. { specifier: '@deepseek-ai/dsh-c', source: './packages/g/c/src' },
  33. ]
  34. const body = renderAliases(aliases, new Set(['@deepseek-ai/dsh-a']))
  35. expect(body).toBe([
  36. ' "@deepseek-ai/dsh-b": ["./packages/g/b/src"]',
  37. ' "@deepseek-ai/dsh-c": ["./packages/g/c/src"]',
  38. ].join(',\n'))
  39. expect(body.endsWith(',')).toBe(false)
  40. })
  41. it('replaces only the marked region', () => {
  42. const text = [
  43. '{ "before": 1,',
  44. ' // BEGIN generated package aliases — pnpm run gen-tsconfig-paths',
  45. ' "stale": ["gone"]',
  46. ' // END generated package aliases',
  47. ' "after": 2 }',
  48. ].join('\n')
  49. const next = writeRegion(text, ' "fresh": ["kept"]')
  50. expect(next).toContain('{ "before": 1,')
  51. expect(next).toContain(' "after": 2 }')
  52. expect(next).toContain('"fresh": ["kept"]')
  53. expect(next).not.toContain('stale')
  54. })
  55. it('refuses a config without the region markers', () => {
  56. expect(() => writeRegion('{}', '')).toThrow('missing the generated-region markers')
  57. })
  58. it('names a package that no alias covers', () => {
  59. // Deleting the group wildcards removed the fallback that used to resolve a
  60. // package nobody had aliased. A package whose name does not match its
  61. // directory is skipped by the generator, so without this check it would
  62. // resolve through the workspace symlink to built lib/types instead.
  63. expect(uncoveredPackages(
  64. ['@deepseek-ai/dsh-a', '@deepseek-ai/dsh-b'],
  65. new Set(['@deepseek-ai/dsh-a']),
  66. )).toEqual(['@deepseek-ai/dsh-b'])
  67. expect(uncoveredPackages(['@deepseek-ai/dsh-a'], new Set(['@deepseek-ai/dsh-a']))).toEqual([])
  68. })
  69. it('covers every workspace package in the committed config', () => {
  70. const config = readFileSync(resolve(root, 'tsconfig.base.json'), 'utf8')
  71. // Includes the packages the generator skips because their name does not
  72. // match their directory: those carry hand-written aliases.
  73. const names = collectPackageNames()
  74. expect(names).toContain('@deepseek-ai/dsh-typert-protocol')
  75. expect(uncoveredPackages(names, mappedSpecifiers(config))).toEqual([])
  76. })
  77. it('leaves no wildcard that probes every package group', () => {
  78. const config = readFileSync(resolve(root, 'tsconfig.base.json'), 'utf8')
  79. // A wildcard lists one candidate per group, so resolving a package late in
  80. // the list costs a filesystem probe — and under tsx a decorated module
  81. // error — for every group before it.
  82. expect(config).not.toContain('"@deepseek-ai/dsh-*":')
  83. })
  84. })