also looking at this
chore(deps): refresh Vue patch and verify packed SSR
#312
4 files
+75
-2
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
Binary file. Nothing to show line by line.
| @@ -31,7 +31,7 @@ | ||
| 31 | 31 | "verify": "bun scripts/verify.ts", |
| 32 | 32 | "verify:packages": "bun scripts/verify-packages.ts", |
| 33 | 33 | "verify:npm": "bun scripts/npm-packages.ts", |
| 34 | "test:packages": "bun test scripts/npm-packages.test.ts scripts/mobile-package.test.ts scripts/svelte-package.test.ts scripts/scaffold-package.test.ts", | |
| 34 | "test:packages": "bun test scripts/npm-packages.test.ts scripts/mobile-package.test.ts scripts/svelte-package.test.ts scripts/vue-package.test.ts scripts/scaffold-package.test.ts", | |
| 35 | 35 | "verify:create": "bun scripts/verify-create-craft.ts", |
| 36 | 36 | "verify:cimports": "bun scripts/audit-cimports.ts", |
| 37 | 37 | "verify:docs": "bun scripts/verify-doc-links.ts", |
| @@ -0,0 +1,73 @@ | ||
| 1 | import { expect, test } from 'bun:test' | |
| 2 | import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' | |
| 3 | import { createRequire } from 'node:module' | |
| 4 | import { tmpdir } from 'node:os' | |
| 5 | import { dirname, join, resolve } from 'node:path' | |
| 6 | ||
| 7 | test('packed Vue composables render on the server and expose usable types', () => { | |
| 8 | const root = resolve(import.meta.dir, '..') | |
| 9 | const packageDir = join(root, 'packages/vue') | |
| 10 | const temp = mkdtempSync(join(tmpdir(), 'craft-vue-package-')) | |
| 11 | const run = (args: string[], cwd = temp): void => { | |
| 12 | const result = Bun.spawnSync(args, { cwd, stdout: 'pipe', stderr: 'pipe', timeout: 30_000 }) | |
| 13 | expect(`${result.stdout}\n${result.stderr}`).not.toContain('error TS') | |
| 14 | expect(result.exitCode).toBe(0) | |
| 15 | } | |
| 16 | try { | |
| 17 | run([process.execPath, 'run', 'build'], packageDir) | |
| 18 | run([process.execPath, 'pm', 'pack', '--filename', join(temp, 'package.tgz')], packageDir) | |
| 19 | const installed = join(temp, 'node_modules/@craft-native/vue') | |
| 20 | mkdirSync(installed, { recursive: true }) | |
| 21 | run(['tar', '-xzf', join(temp, 'package.tgz'), '--strip-components=1', '-C', installed]) | |
| 22 | // Supply the actual installed peer, not a mock or a workspace Craft import. | |
| 23 | const require = createRequire(join(packageDir, 'package.json')) | |
| 24 | symlinkSync(dirname(require.resolve('vue/package.json')), join(temp, 'node_modules/vue'), 'junction') | |
| 25 | writeFileSync(join(temp, 'consumer.mjs'), ` | |
| 26 | import { strict as assert } from 'node:assert' | |
| 27 | import { createSSRApp, h } from 'vue' | |
| 28 | import { renderToString } from 'vue/server-renderer' | |
| 29 | import { useCraft, usePlatform } from '@craft-native/vue' | |
| 30 | assert.equal(typeof window, 'undefined') | |
| 31 | globalThis.setInterval = () => { throw new Error('SSR must not start bridge polling') } | |
| 32 | async function render(override) { | |
| 33 | return renderToString(createSSRApp({ | |
| 34 | setup() { | |
| 35 | const { craft, isReady } = useCraft() | |
| 36 | const { platform, loading, error } = usePlatform() | |
| 37 | assert.equal(craft.value, null) | |
| 38 | assert.equal(isReady.value, false) | |
| 39 | assert.equal(platform.value, null) | |
| 40 | assert.equal(loading.value, true) | |
| 41 | assert.equal(error.value, null) | |
| 42 | if (override) isReady.value = true | |
| 43 | return () => h('p', { 'data-ready': String(isReady.value) }, '<script>untrusted</script>') | |
| 44 | } | |
| 45 | })) | |
| 46 | } | |
| 47 | const first = await render(true) | |
| 48 | const second = await render(false) | |
| 49 | assert.ok(first.includes('data-ready="true"')) | |
| 50 | assert.ok(second.includes('data-ready="false"')) | |
| 51 | assert.ok(second.includes('<script>untrusted</script>')) | |
| 52 | assert.ok(!second.includes('<script>')) | |
| 53 | `) | |
| 54 | run([process.execPath, 'consumer.mjs']) | |
| 55 | writeFileSync(join(temp, 'consumer.mts'), ` | |
| 56 | import type { Ref } from 'vue' | |
| 57 | import { useCraft, usePlatform } from '@craft-native/vue' | |
| 58 | const ready: Ref<boolean> = useCraft().isReady | |
| 59 | const platform: Ref<{ platform: string; version: string } | null> = usePlatform().platform | |
| 60 | // @ts-expect-error readiness is boolean, not text | |
| 61 | const wrong: Ref<string> = useCraft().isReady | |
| 62 | void [ready, platform, wrong] | |
| 63 | `) | |
| 64 | run([ | |
| 65 | process.execPath, join(root, 'packages/typescript/scripts/tsc.ts'), | |
| 66 | 'consumer.mts', '--noEmit', '--strict', '--module', 'nodenext', | |
| 67 | '--moduleResolution', 'nodenext', '--target', 'esnext', '--ignoreConfig', | |
| 68 | ]) | |
| 69 | } | |
| 70 | finally { | |
| 71 | rmSync(temp, { recursive: true, force: true }) | |
| 72 | } | |
| 73 | }, 60_000) | |