dev-web.spec.ts 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { expect, it } from 'vitest'
  5. import type { TsdownBundle } from 'tsdown'
  6. import { discoverLibraryDirs, discoverPluginDirs, watchClientPlugins } from './dev-web.ts'
  7. it('discovers dsh.client packages with sibling roles', async () => {
  8. const root = await mkdtemp(join(tmpdir(), 'dsh-dev-web-discovery-'))
  9. try {
  10. const current = join(root, 'packages', 'client', 'current')
  11. await mkdir(current, { recursive: true })
  12. await writeFile(join(current, 'package.json'), JSON.stringify({
  13. dsh: {
  14. bundle: { patch: './cordis.patch.yml' },
  15. client: { platform: 'web' },
  16. profile: { bundles: [] },
  17. },
  18. }))
  19. expect(discoverPluginDirs(root)).toEqual(['packages/client/current'])
  20. } finally {
  21. await rm(root, { recursive: true, force: true })
  22. }
  23. })
  24. it('discovers client-preset packages the shell links, excluding loader-delivered and test infrastructure', async () => {
  25. const root = await mkdtemp(join(tmpdir(), 'dsh-dev-web-library-'))
  26. try {
  27. const write = async (dir: string, manifest: unknown, config: string): Promise<void> => {
  28. await mkdir(join(root, dir), { recursive: true })
  29. await writeFile(join(root, dir, 'package.json'), JSON.stringify(manifest))
  30. await writeFile(join(root, dir, 'tsdown.config.ts'), config)
  31. }
  32. const clientPreset = "import { clientLibrary } from '../tsdown.client.ts'\nexport default clientLibrary('x', [])\n"
  33. // Linked by the compile shell: client preset, no loader-delivered half.
  34. await write('packages/client/linked', {}, clientPreset)
  35. // Loader-delivered: discoverPluginDirs owns it, so it must not appear twice.
  36. await write('packages/client/delivered', { dsh: { client: { platform: 'web' } } }, clientPreset)
  37. // Test infrastructure builds through the preset but never enters the shell graph.
  38. await write('packages/test-support/harness', {}, clientPreset)
  39. // Host package with its own config: not a client-face build at all.
  40. await write('packages/host/server', {}, "import { defineConfig } from 'tsdown'\nexport default defineConfig({})\n")
  41. expect(discoverLibraryDirs(root)).toEqual(['packages/client/linked'])
  42. } finally {
  43. await rm(root, { recursive: true, force: true })
  44. }
  45. })
  46. it('rebuilds a client-plugin bundle after its source changes', async () => {
  47. const root = await mkdtemp(join(tmpdir(), 'dsh-dev-web-watch-'))
  48. let bundles: TsdownBundle[] = []
  49. try {
  50. await symlink(join(import.meta.dirname, '..', 'node_modules'), join(root, 'node_modules'), 'dir')
  51. await writeFile(join(root, 'package.json'), JSON.stringify({ name: '@dsh-test/dev-web-watch', private: true, type: 'module' }))
  52. await writeFile(join(root, 'tsdown.config.ts'), `
  53. import { defineConfig } from 'tsdown'
  54. export default defineConfig({
  55. entry: { client: 'src.ts' }, outDir: 'lib', format: 'cjs', platform: 'browser', dts: false, clean: false,
  56. outputOptions: { entryFileNames: 'client.js' },
  57. })
  58. `)
  59. const sourcePath = join(root, 'src.ts')
  60. const bundlePath = join(root, 'lib/client.js')
  61. await writeFile(sourcePath, 'export const version = "watch-v1"\n')
  62. bundles = await watchClientPlugins(root, ['.'], 50)
  63. expect(await readFile(bundlePath, 'utf8')).toContain('watch-v1')
  64. await new Promise(resolve => setTimeout(resolve, 1_000))
  65. await writeFile(sourcePath, `export const version = "watch-v2-${'x'.repeat(100)}"\n`)
  66. await expect.poll(async () => (await readFile(bundlePath, 'utf8')).includes('watch-v2-'), {
  67. timeout: 10_000,
  68. }).toBe(true)
  69. } finally {
  70. for (const bundle of bundles) await bundle[Symbol.asyncDispose]()
  71. await rm(root, { recursive: true, force: true })
  72. }
  73. }, 20_000)