snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
250
apps/dotcom/client/scripts/measure-bundle-size.test.ts
Normal file
250
apps/dotcom/client/scripts/measure-bundle-size.test.ts
Normal file
@@ -0,0 +1,250 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { gzipSync } from 'zlib'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
BundleAsset,
|
||||
getAssetCategory,
|
||||
getBundleSizeEvents,
|
||||
getInitialAssetNames,
|
||||
measureBundleSize,
|
||||
stripContentHash,
|
||||
summarizeAssets,
|
||||
} from './measure-bundle-size'
|
||||
|
||||
describe('getAssetCategory', () => {
|
||||
it('categorizes assets by extension', () => {
|
||||
expect(getAssetCategory('index-BdIhNi0J.js')).toBe('js')
|
||||
expect(getAssetCategory('index-BdIhNi0J.css')).toBe('css')
|
||||
expect(getAssetCategory('IBMPlexSans-Medium-CxYz1234.woff2')).toBe('font')
|
||||
expect(getAssetCategory('0_merged-abcd1234.svg')).toBe('image')
|
||||
expect(getAssetCategory('en-abcd1234.json')).toBe('json')
|
||||
expect(getAssetCategory('something-abcd1234.wasm')).toBe('other')
|
||||
})
|
||||
})
|
||||
|
||||
describe('stripContentHash', () => {
|
||||
it('removes the content hash so a chunk is comparable across builds', () => {
|
||||
expect(stripContentHash('index-BdIhNi0J.js')).toBe('index.js')
|
||||
expect(stripContentHash('TlaEditor-_a1B2c3D.js')).toBe('TlaEditor.js')
|
||||
expect(stripContentHash('IBMPlexSans-Medium-CxYz1234.woff2')).toBe('IBMPlexSans-Medium.woff2')
|
||||
})
|
||||
|
||||
it('leaves names without a hash alone', () => {
|
||||
expect(stripContentHash('index.js')).toBe('index.js')
|
||||
expect(stripContentHash('IBMPlexSans-Medium.woff2')).toBe('IBMPlexSans-Medium.woff2')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getInitialAssetNames', () => {
|
||||
it('collects every asset index.html references', () => {
|
||||
const html = `<!doctype html><html><head>
|
||||
<script type="module" crossorigin src="/assets/index-BdIhNi0J.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/vendor-CxYz1234.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DeFg5678.css">
|
||||
<link rel="preload" href="/assets/IBMPlexSans-Medium-HiJk9012.woff2" as="font">
|
||||
<link rel="preload" href="/assets/0_merged-LmNo3456.svg" as="image">
|
||||
<link rel="icon" href="/favicon.svg">
|
||||
</head><body></body></html>`
|
||||
|
||||
expect(getInitialAssetNames(html)).toEqual(
|
||||
new Set([
|
||||
'index-BdIhNi0J.js',
|
||||
'vendor-CxYz1234.js',
|
||||
'index-DeFg5678.css',
|
||||
'IBMPlexSans-Medium-HiJk9012.woff2',
|
||||
'0_merged-LmNo3456.svg',
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('ignores query strings and assets outside /assets', () => {
|
||||
const html = `<script src="/assets/index-BdIhNi0J.js?v=1"></script><img src="/logo.png">`
|
||||
expect(getInitialAssetNames(html)).toEqual(new Set(['index-BdIhNi0J.js']))
|
||||
})
|
||||
})
|
||||
|
||||
describe('summarizeAssets', () => {
|
||||
function asset(overrides: Partial<BundleAsset>): BundleAsset {
|
||||
return {
|
||||
fileName: 'index-BdIhNi0J.js',
|
||||
name: 'index.js',
|
||||
category: 'js',
|
||||
bytes: 100,
|
||||
gzipBytes: 40,
|
||||
isInitial: false,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
const noAssets = {
|
||||
font: { count: 0, bytes: 0, gzipBytes: 0 },
|
||||
image: { count: 0, bytes: 0, gzipBytes: 0 },
|
||||
json: { count: 0, bytes: 0, gzipBytes: 0 },
|
||||
other: { count: 0, bytes: 0, gzipBytes: 0 },
|
||||
}
|
||||
|
||||
it('totals assets overall, by category, and across the initial payload', () => {
|
||||
const report = summarizeAssets([
|
||||
asset({ bytes: 100, gzipBytes: 40, isInitial: true }),
|
||||
asset({ bytes: 200, gzipBytes: 60 }),
|
||||
asset({ category: 'css', bytes: 50, gzipBytes: 20, isInitial: true }),
|
||||
])
|
||||
|
||||
expect(report.total).toEqual({ count: 3, bytes: 350, gzipBytes: 120 })
|
||||
expect(report.initial).toEqual({ count: 2, bytes: 150, gzipBytes: 60 })
|
||||
expect(report.byCategory).toEqual({
|
||||
js: { count: 2, bytes: 300, gzipBytes: 100 },
|
||||
css: { count: 1, bytes: 50, gzipBytes: 20 },
|
||||
...noAssets,
|
||||
})
|
||||
expect(report.initialByCategory).toEqual({
|
||||
js: { count: 1, bytes: 100, gzipBytes: 40 },
|
||||
css: { count: 1, bytes: 50, gzipBytes: 20 },
|
||||
...noAssets,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('measureBundleSize', () => {
|
||||
let staticDir: string
|
||||
|
||||
beforeEach(() => {
|
||||
staticDir = mkdtempSync(join(tmpdir(), 'bundle-size-'))
|
||||
mkdirSync(join(staticDir, 'assets'))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(staticDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
function writeAsset(fileName: string, contents: string) {
|
||||
writeFileSync(join(staticDir, 'assets', fileName), contents)
|
||||
}
|
||||
|
||||
it('measures raw and gzipped sizes, and marks assets index.html loads as initial', () => {
|
||||
const entry = 'a'.repeat(1000)
|
||||
const lazy = 'b'.repeat(500)
|
||||
writeAsset('index-BdIhNi0J.js', entry)
|
||||
writeAsset('TlaEditor-CxYz1234.js', lazy)
|
||||
writeFileSync(
|
||||
join(staticDir, 'index.html'),
|
||||
`<script type="module" src="/assets/index-BdIhNi0J.js"></script>`
|
||||
)
|
||||
|
||||
const report = measureBundleSize(staticDir)
|
||||
|
||||
expect(report.assets).toEqual([
|
||||
{
|
||||
fileName: 'index-BdIhNi0J.js',
|
||||
name: 'index.js',
|
||||
category: 'js',
|
||||
bytes: 1000,
|
||||
gzipBytes: gzipSync(entry).byteLength,
|
||||
isInitial: true,
|
||||
},
|
||||
{
|
||||
fileName: 'TlaEditor-CxYz1234.js',
|
||||
name: 'TlaEditor.js',
|
||||
category: 'js',
|
||||
bytes: 500,
|
||||
gzipBytes: gzipSync(lazy).byteLength,
|
||||
isInitial: false,
|
||||
},
|
||||
])
|
||||
expect(report.initial.bytes).toBe(1000)
|
||||
})
|
||||
|
||||
it('skips source maps, which are only downloaded with devtools open', () => {
|
||||
writeAsset('index-BdIhNi0J.js', 'console.log(1)')
|
||||
writeAsset('index-BdIhNi0J.js.map', 'x'.repeat(10000))
|
||||
writeFileSync(join(staticDir, 'index.html'), '<html></html>')
|
||||
|
||||
const report = measureBundleSize(staticDir)
|
||||
|
||||
expect(report.assets.map((asset) => asset.fileName)).toEqual(['index-BdIhNi0J.js'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getBundleSizeEvents', () => {
|
||||
it('reports totals once and the largest assets individually', () => {
|
||||
const report = summarizeAssets([
|
||||
{
|
||||
fileName: 'index-BdIhNi0J.js',
|
||||
name: 'index.js',
|
||||
category: 'js',
|
||||
bytes: 100,
|
||||
gzipBytes: 40,
|
||||
isInitial: true,
|
||||
},
|
||||
])
|
||||
|
||||
const [summary, ...assets] = getBundleSizeEvents(report, { git_commit: 'abc123' })
|
||||
|
||||
expect(summary).toEqual({
|
||||
event: 'dotcom_bundle_size',
|
||||
distinctId: 'dotcom-bundle-size',
|
||||
properties: {
|
||||
git_commit: 'abc123',
|
||||
total_bytes: 100,
|
||||
total_gzip_bytes: 40,
|
||||
total_count: 1,
|
||||
initial_bytes: 100,
|
||||
initial_gzip_bytes: 40,
|
||||
initial_count: 1,
|
||||
js_bytes: 100,
|
||||
js_gzip_bytes: 40,
|
||||
js_count: 1,
|
||||
initial_js_bytes: 100,
|
||||
initial_js_gzip_bytes: 40,
|
||||
initial_js_count: 1,
|
||||
css_bytes: 0,
|
||||
css_gzip_bytes: 0,
|
||||
css_count: 0,
|
||||
initial_css_bytes: 0,
|
||||
initial_css_gzip_bytes: 0,
|
||||
initial_css_count: 0,
|
||||
font_bytes: 0,
|
||||
font_gzip_bytes: 0,
|
||||
font_count: 0,
|
||||
initial_font_bytes: 0,
|
||||
initial_font_gzip_bytes: 0,
|
||||
initial_font_count: 0,
|
||||
image_bytes: 0,
|
||||
image_gzip_bytes: 0,
|
||||
image_count: 0,
|
||||
initial_image_bytes: 0,
|
||||
initial_image_gzip_bytes: 0,
|
||||
initial_image_count: 0,
|
||||
json_bytes: 0,
|
||||
json_gzip_bytes: 0,
|
||||
json_count: 0,
|
||||
initial_json_bytes: 0,
|
||||
initial_json_gzip_bytes: 0,
|
||||
initial_json_count: 0,
|
||||
other_bytes: 0,
|
||||
other_gzip_bytes: 0,
|
||||
other_count: 0,
|
||||
initial_other_bytes: 0,
|
||||
initial_other_gzip_bytes: 0,
|
||||
initial_other_count: 0,
|
||||
},
|
||||
})
|
||||
expect(assets).toEqual([
|
||||
{
|
||||
event: 'dotcom_bundle_size_asset',
|
||||
distinctId: 'dotcom-bundle-size',
|
||||
properties: {
|
||||
git_commit: 'abc123',
|
||||
asset_name: 'index.js',
|
||||
asset_file_name: 'index-BdIhNi0J.js',
|
||||
asset_category: 'js',
|
||||
asset_is_initial: true,
|
||||
bytes: 100,
|
||||
gzip_bytes: 40,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user