[READ-ONLY] Mirror of https://github.com/danielroe/cross-origin-storage. Load shared dependencies from Cross-Origin Storage (COS).
cross-origin-storage
experimental
nuxt
vite
vite-plugin
1import { execSync } from 'node:child_process'
2import { createHash } from 'node:crypto'
3import { readFileSync, readdirSync, rmSync } from 'node:fs'
4import { fileURLToPath } from 'node:url'
5import { join } from 'node:path'
6import { beforeAll, describe, expect, it } from 'vitest'
7
8const fixtureDir = fileURLToPath(new URL('./fixtures/basic', import.meta.url))
9const publicNuxt = join(fixtureDir, '.output/public/_nuxt')
10
11function build(): void {
12 rmSync(join(fixtureDir, '.output'), { recursive: true, force: true })
13 rmSync(join(fixtureDir, '.nuxt'), { recursive: true, force: true })
14 execSync('npx nuxi build', { cwd: fixtureDir, stdio: 'inherit' })
15}
16
17function cosChunks(): string[] {
18 return readdirSync(publicNuxt).filter(f => /^[a-f0-9]{64}\.js$/.test(f))
19}
20
21function specifiersOf(file: string): string[] {
22 const code = readFileSync(join(publicNuxt, file), 'utf8')
23 const specifiers = [...code.matchAll(/(?:from|import)\s*["']([^"']+)["']/g)].map(m => m[1]!)
24 return [...new Set(specifiers)]
25}
26
27describe('cos build output', () => {
28 beforeAll(build, 240_000)
29
30 it('emits one content-addressed chunk per managed vue package', () => {
31 // vue + runtime-dom + runtime-core + reactivity + shared
32 expect(cosChunks()).toHaveLength(5)
33 })
34
35 it('names every chunk after the sha-256 of its bytes', () => {
36 for (const file of cosChunks()) {
37 const hash = file.replace('.js', '')
38 const actual = createHash('sha256').update(readFileSync(join(publicNuxt, file))).digest('hex')
39 expect(actual).toBe(hash)
40 }
41 })
42
43 it('references dependencies only by content-addressed specifier', () => {
44 for (const file of cosChunks()) {
45 for (const specifier of specifiersOf(file)) {
46 expect(specifier, `${file} imports non-content-addressed ${specifier}`).toMatch(/^cos1:[a-f0-9]{64}$/)
47 }
48 }
49 })
50
51 it('externalises shared dependencies instead of inlining them (no duplication)', () => {
52 const sizes = cosChunks().map(f => readFileSync(join(publicNuxt, f)).length)
53 // A self-contained vue would be ~150KB minified; externalised it is tiny.
54 expect(Math.min(...sizes)).toBeLessThan(1_000)
55 })
56
57 it('keeps the reactivity singleton as a single shared leaf chunk', () => {
58 const chunks = cosChunks()
59 const leaves = chunks.filter(f => specifiersOf(f).length === 0)
60 // @vue/shared is the only package that imports nothing; if it were
61 // duplicated or inlined there would be zero or several leaves.
62 expect(leaves, 'expected exactly one dependency-free leaf (@vue/shared)').toHaveLength(1)
63
64 const leafSpecifier = `cos1:${leaves[0]!.replace('.js', '')}`
65 const directDependants = chunks.filter(f => specifiersOf(f).includes(leafSpecifier))
66 // runtime-dom, runtime-core and reactivity all import @vue/shared directly.
67 expect(directDependants.length).toBeGreaterThanOrEqual(3)
68 })
69
70 it('leaves no machine-specific paths in any chunk', () => {
71 for (const file of cosChunks()) {
72 const code = readFileSync(join(publicNuxt, file), 'utf8')
73 expect(code, `${file} leaks a path`).not.toMatch(/node_modules|cos-ext:|#region/)
74 }
75 })
76
77 it('produces identical hashes when rebuilt (deterministic)', () => {
78 const first = cosChunks().sort()
79 build()
80 expect(cosChunks().sort()).toEqual(first)
81 }, 240_000)
82})