| @@ -31,3 +31,57 @@ test('packed Svelte condition resolves outside the workspace', async () => { |
| 31 | 31 | rmSync(temp, { recursive: true, force: true }) |
| 32 | 32 | } |
| 33 | 33 | }) |
| 34 | + |
| 35 | +test('store declarations expose the public Readable contract', () => { |
| 36 | + const root = resolve(import.meta.dir, '..') |
| 37 | + const temp = mkdtempSync(join(tmpdir(), 'craft-svelte-types-')) |
| 38 | + const run = (args: string[], cwd = temp): void => { |
| 39 | + const result = Bun.spawnSync([ |
| 40 | + process.execPath, join(root, 'packages/typescript/scripts/tsc.ts'), ...args, |
| 41 | + ], { cwd, stdout: 'pipe', stderr: 'pipe' }) |
| 42 | + expect(`${result.stdout}\n${result.stderr}`).not.toContain('error TS') |
| 43 | + expect(result.exitCode).toBe(0) |
| 44 | + } |
| 45 | + try { |
| 46 | + run([ |
| 47 | + '-p', join(root, 'packages/svelte/tsconfig.json'), '--emitDeclarationOnly', |
| 48 | + '--outDir', join(temp, 'declarations'), |
| 49 | + ]) |
| 50 | + |
| 51 | + |
| 52 | + writeFileSync(join(temp, 'store-contract.d.ts'), ` |
| 53 | +declare module 'svelte/store' { |
| 54 | + export interface Readable<T> { |
| 55 | + subscribe(run: (value: T) => void, invalidate?: (value?: T) => void): () => void |
| 56 | + } |
| 57 | +} |
| 58 | +`) |
| 59 | + writeFileSync(join(temp, 'consumer.mts'), ` |
| 60 | +import type { Readable } from 'svelte/store' |
| 61 | +import { craft, isReady } from './declarations/stores/craft.js' |
| 62 | +import { platform, createPlatformStore } from './declarations/stores/platform.js' |
| 63 | +const ready: Readable<boolean> = isReady |
| 64 | +const info: Readable<{ platform: string; version: string } | null> = platform |
| 65 | +const another: typeof info = createPlatformStore() |
| 66 | +const unsubscribe: () => void = craft.subscribe(api => { |
| 67 | + if (api) { |
| 68 | + const result: Promise<{ platform: string; version: string }> = api.getPlatform() |
| 69 | + void result |
| 70 | + } |
| 71 | +}) |
| 72 | + |
| 73 | +craft.set(null) |
| 74 | + |
| 75 | +const wrong: Readable<string> = isReady |
| 76 | +void [ready, info, another, unsubscribe, wrong] |
| 77 | +`) |
| 78 | + run([ |
| 79 | + 'consumer.mts', 'store-contract.d.ts', '--noEmit', '--strict', |
| 80 | + '--module', 'nodenext', '--moduleResolution', 'nodenext', |
| 81 | + '--target', 'esnext', '--ignoreConfig', |
| 82 | + ]) |
| 83 | + } |
| 84 | + finally { |
| 85 | + rmSync(temp, { recursive: true, force: true }) |
| 86 | + } |
| 87 | +}, 60_000) |