Skip to content

Commit 15bb7bb

Browse files
authored
test: bundle size (#3877)
1 parent 4dfd4b1 commit 15bb7bb

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
.nitro
1818
.output
1919
.eslintcache
20+
.tmp
2021

2122
dist
2223
temp
@@ -28,5 +29,4 @@ node_modules
2829

2930
package-lock.json
3031
test/setup/context.json
31-
eslint-typegen.d.ts
32-
.eslintcache
32+
eslint-typegen.d.ts

test/bundle.test.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// adapted from https://github.com/nuxt/image/blob/5415ba721e8cb1ec15205f9bf54ada2e3d5fe07d/test/unit/bundle.test.ts
2+
import process from "node:process";
3+
import { join } from "node:path";
4+
import { fileURLToPath } from "node:url";
5+
import { mkdir, writeFile, rm, lstat, readFile } from "node:fs/promises";
6+
import { buildNuxt, loadNuxt } from "@nuxt/kit";
7+
import type { NuxtConfig } from "@nuxt/schema";
8+
import { describe, it, expect } from "vitest";
9+
import { glob } from "tinyglobby";
10+
import { isWindows } from "std-env";
11+
import { defu } from "defu";
12+
13+
describe.skipIf(process.env.ECOSYSTEM_CI || isWindows)(
14+
"nuxt i18n bundle size",
15+
() => {
16+
it("should match snapshot", { timeout: 120_000 }, async () => {
17+
const rootDir = fileURLToPath(new URL("../.tmp", import.meta.url));
18+
19+
await rm(rootDir, { recursive: true, force: true }).catch(() => null);
20+
21+
const [base, withModule, withVueI18n, withVueI18nDropCompiler] =
22+
await Promise.all([
23+
build(join(rootDir, "without")),
24+
build(join(rootDir, "with"), {
25+
modules: ["@nuxtjs/i18n"],
26+
// i18n: {
27+
// defaultLocale: "en",
28+
// locales: [
29+
// { code: "en", file: "en.json" },
30+
// { code: "fr", file: "fr.json" },
31+
// ],
32+
// },
33+
}),
34+
build(join(rootDir, "vue-i18n"), {}, { vueI18n: true }),
35+
build(
36+
join(rootDir, "vue-i18n-drop-compiler"),
37+
{
38+
vite: {
39+
define: {
40+
__INTLIFY_JIT_COMPILATION__: true,
41+
__INTLIFY_DROP_MESSAGE_COMPILER__: true,
42+
},
43+
},
44+
},
45+
{ vueI18n: true },
46+
),
47+
]);
48+
49+
const data = {
50+
// total bundle size increase
51+
module: roundToKilobytes(withModule.totalBytes - base.totalBytes),
52+
"module (without vue-i18n)": roundToKilobytes(withModule.totalBytes - withVueI18n.totalBytes),
53+
"vue-i18n": roundToKilobytes(withVueI18n.totalBytes - base.totalBytes),
54+
"vue-i18n (without message compiler)": roundToKilobytes(withVueI18nDropCompiler.totalBytes - base.totalBytes),
55+
};
56+
57+
expect(data).toMatchInlineSnapshot(`
58+
{
59+
"module": "69.4k",
60+
"module (without vue-i18n)": "26.0k",
61+
"vue-i18n": "43.3k",
62+
"vue-i18n (without message compiler)": "26.8k",
63+
}
64+
`);
65+
});
66+
},
67+
);
68+
69+
async function build(
70+
rootDir: string,
71+
config: NuxtConfig = {},
72+
options: { vueI18n?: boolean } = {},
73+
) {
74+
await mkdir(rootDir, { recursive: true });
75+
76+
const tree: Record<string, string> = {
77+
// "/i18n/locales/en.json": `{ "hello": "Hello" }`,
78+
// "/i18n/locales/fr.json": `{ "hello": "Bonjour" }`,
79+
// "/pages/index.vue": `<template>{{ $t('hello') }}</template>`,
80+
"/app.vue": `<template><NuxtPage /></template>`,
81+
};
82+
83+
if (options.vueI18n) {
84+
const template = [
85+
`<script setup lang="ts">`,
86+
`import { useI18n } from 'vue-i18n'`,
87+
`const { t } = useI18n()`,
88+
`</script>`,
89+
`<template><div>{{ t('hello') }}</div></template>`,
90+
].join("\n");
91+
92+
if (tree["/pages/index.vue"]) {
93+
tree["/pages/index.vue"] = template;
94+
} else {
95+
tree["/app.vue"] = template;
96+
}
97+
}
98+
99+
for (const [path, content] of Object.entries(tree)) {
100+
const fullPath = join(rootDir, path);
101+
const dir = join(fullPath, "..");
102+
if (dir !== rootDir) {
103+
await mkdir(join(fullPath, ".."), { recursive: true });
104+
}
105+
await writeFile(fullPath, content);
106+
}
107+
108+
const nuxt = await loadNuxt({
109+
cwd: rootDir,
110+
ready: true,
111+
overrides: {
112+
// ssr: false,
113+
...defu(config, {
114+
vite: {
115+
define: {
116+
__INTLIFY_PROD_DEVTOOLS__: false, // for vue-i18n build - disabled in nuxt-i18n bundler.ts
117+
},
118+
// to disable minification for easier size analysis
119+
// $client: {
120+
// build: {
121+
// minify: false,
122+
// rollupOptions: {
123+
// output: {
124+
// chunkFileNames: "_nuxt/[name].js",
125+
// entryFileNames: "_nuxt/[name].js",
126+
// },
127+
// },
128+
// },
129+
// },
130+
},
131+
}),
132+
},
133+
});
134+
await buildNuxt(nuxt);
135+
await nuxt.close();
136+
return await analyzeSizes(["**/*.js"], join(rootDir, ".output/public"));
137+
}
138+
139+
async function analyzeSizes(pattern: string[], rootDir: string) {
140+
const files: string[] = await glob(pattern, { cwd: rootDir });
141+
let totalBytes = 0;
142+
for (const file of files) {
143+
const path = join(rootDir, file);
144+
const isSymlink = (await lstat(path).catch(() => null))?.isSymbolicLink();
145+
146+
if (!isSymlink) {
147+
const bytes = Buffer.byteLength(await readFile(path));
148+
totalBytes += bytes;
149+
}
150+
}
151+
return { files, totalBytes };
152+
}
153+
154+
function roundToKilobytes(bytes: number) {
155+
return (bytes / 1024).toFixed(bytes > 100 * 1024 ? 0 : 1) + "k";
156+
}

0 commit comments

Comments
 (0)