publint-all.spec.ts 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
  2. import { tmpdir } from 'node:os'
  3. import { join } from 'node:path'
  4. import { fileURLToPath } from 'node:url'
  5. import { spawnSync } from 'node:child_process'
  6. import { afterEach, describe, expect, it } from 'vitest'
  7. const repositoryRoot = fileURLToPath(new URL('..', import.meta.url))
  8. const runner = fileURLToPath(new URL('./publint-all.ts', import.meta.url))
  9. const roots: string[] = []
  10. afterEach(() => {
  11. for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
  12. })
  13. function fixture(options: {
  14. exportPath?: string
  15. indexSource?: string
  16. files?: Record<string, string>
  17. } = {}): string {
  18. const root = mkdtempSync(join(tmpdir(), 'dsh-publint-all-'))
  19. roots.push(root)
  20. const packageDir = join(root, 'packages/core/probe')
  21. mkdirSync(join(packageDir, 'lib'), { recursive: true })
  22. writeFileSync(join(packageDir, 'package.json'), `${JSON.stringify({
  23. name: '@deepseek-ai/dsh-probe',
  24. version: '0.0.1',
  25. type: 'module',
  26. license: 'MIT',
  27. engines: { node: '>=22.19' },
  28. sideEffects: false,
  29. files: ['lib'],
  30. exports: { '.': { default: options.exportPath ?? './lib/index.js' } },
  31. }, null, 2)}\n`)
  32. writeFileSync(join(packageDir, 'README.md'), '# Probe\n')
  33. writeFileSync(join(packageDir, 'lib/index.js'), options.indexSource ?? 'export const probe = true\n')
  34. for (const [path, source] of Object.entries(options.files ?? {})) {
  35. mkdirSync(join(packageDir, path, '..'), { recursive: true })
  36. writeFileSync(join(packageDir, path), source)
  37. }
  38. writeFileSync(join(packageDir, 'unpublished.js'), 'export const hidden = true\n')
  39. return root
  40. }
  41. function run(root: string) {
  42. return spawnSync(process.execPath, [
  43. '--import', 'tsx', runner,
  44. '--packages-root', root,
  45. ], {
  46. cwd: repositoryRoot,
  47. encoding: 'utf8',
  48. timeout: 5_000,
  49. })
  50. }
  51. describe('publint package runner', () => {
  52. it('lints recursively declared files from an in-memory publication view', () => {
  53. const result = run(fixture())
  54. expect(result.status, result.stderr).toBe(0)
  55. expect(result.stdout).toContain('linting 1 package(s)')
  56. expect(result.stdout).toContain('All good!')
  57. })
  58. it('rejects an export that exists in the workspace but is not published', () => {
  59. const result = run(fixture({ exportPath: './unpublished.js' }))
  60. expect(result.status).toBe(1)
  61. expect(result.stdout).toContain('unpublished.js')
  62. })
  63. it('rejects a public export whose built file is missing', () => {
  64. const result = run(fixture({ exportPath: './lib/missing.js' }))
  65. expect(result.status).toBe(1)
  66. expect(result.stdout).toContain('missing.js')
  67. })
  68. it('accepts published relative JavaScript and CSS targets', () => {
  69. const result = run(fixture({
  70. indexSource: "export { helper } from './helper.js'\nimport './theme.css'\n",
  71. files: {
  72. 'lib/helper.js': 'export const helper = true\n',
  73. 'lib/theme.css': ':root {}\n',
  74. },
  75. }))
  76. expect(result.status, result.stderr).toBe(0)
  77. })
  78. it('rejects unpublished relative JavaScript and CSS targets', () => {
  79. const result = run(fixture({
  80. indexSource: "export { helper } from './missing.js'\nimport './missing.css'\n",
  81. }))
  82. expect(result.status).toBe(1)
  83. expect(result.stderr).toContain('imports "./missing.js"')
  84. expect(result.stderr).toContain('imports "./missing.css"')
  85. })
  86. })