From 4931c986de86c1b3082a265f8c30dff90948155a Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 12 Oct 2022 15:35:57 +0100 Subject: [PATCH 1/6] test: add bundle size test --- test/bundle.test.ts | 91 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 test/bundle.test.ts diff --git a/test/bundle.test.ts b/test/bundle.test.ts new file mode 100644 index 00000000000..d003e6cff91 --- /dev/null +++ b/test/bundle.test.ts @@ -0,0 +1,91 @@ +import { fileURLToPath } from 'node:url' +import fsp from 'node:fs/promises' +import { beforeAll, describe, expect, it } from 'vitest' +import { execaCommand } from 'execa' +import { globby } from 'globby' +import { join } from 'pathe' + +const rootDir = fileURLToPath(new URL('..', import.meta.url)) +const publicDir = join(rootDir, '.output/public') +const serverDir = join(rootDir, '.output/server') + +describe('minimal nuxt application', () => { + beforeAll(async () => { + await execaCommand('yarn nuxi build', { + cwd: rootDir + }) + }, 120 * 1000) + + it('default client bundle size', async () => { + const { files, size } = await measure('**/*.js', publicDir) + expect(size).toBeLessThan(110000) + // expect(size).toMatchInlineSnapshot('106916') + expect(files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` + [ + "_nuxt/entry.js", + "_nuxt/error-404.js", + "_nuxt/error-500.js", + "_nuxt/error-component.js", + ] + `) + }) + + it('default server bundle size', async () => { + const serverBundle = await measure(['**/*.mjs', '!node_modules'], serverDir) + expect(serverBundle.size).toBeLessThan(120000) + // expect(serverBundle.size).toMatchInlineSnapshot('114018') + + const modules = await measure('node_modules/**/*', serverDir) + expect(modules.size).toBeLessThan(2700000) + // expect(modules.size).toMatchInlineSnapshot('2637251') + + const packages = modules.files + .filter(m => m.endsWith('package.json')) + .map(m => m.replace('/package.json', '').replace('node_modules/', '')) + .sort() + expect(packages).toMatchInlineSnapshot(` + [ + "@babel/parser", + "@vue/compiler-core", + "@vue/compiler-dom", + "@vue/compiler-ssr", + "@vue/reactivity", + "@vue/runtime-core", + "@vue/runtime-dom", + "@vue/server-renderer", + "@vue/shared", + "buffer-from", + "cookie-es", + "defu", + "destr", + "estree-walker", + "h3", + "hookable", + "node-fetch-native", + "ohash", + "ohmyfetch", + "pathe", + "radix3", + "scule", + "source-map", + "source-map-support", + "ufo", + "unctx", + "unenv", + "unstorage", + "vue", + "vue-bundle-renderer", + ] + `) + }) +}) + +async function measure (pattern: string | string[], rootDir: string) { + const files = await globby(pattern, { cwd: rootDir }) + let size = 0 + for (const file of files) { + const stats = await fsp.stat(join(rootDir, file)) + size += stats.size + } + return { files, size } +} From 2afec349e3be406e51352663f4ee58ade8a21b21 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 12 Oct 2022 15:36:53 +0100 Subject: [PATCH 2/6] fix(nuxt): remove vue-router dependency from `runLegacyAsyncData` --- packages/nuxt/src/app/components/nuxt-link.ts | 4 ++-- packages/nuxt/src/app/composables/component.ts | 2 +- packages/nuxt/src/pages/runtime/page.ts | 4 ++-- packages/nuxt/src/pages/runtime/utils.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index baab6d0b9cb..87be9a5abec 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -1,11 +1,11 @@ import { defineComponent, h, ref, resolveComponent, PropType, computed, DefineComponent, ComputedRef, onMounted, onBeforeUnmount } from 'vue' -import { RouteLocationRaw, Router } from 'vue-router' +import type { RouteLocationRaw, Router } from 'vue-router' import { hasProtocol } from 'ufo' import { navigateTo, useRouter } from '../composables/router' import { useNuxtApp } from '../nuxt' -const firstNonUndefined = (...args: (T | undefined)[]) => args.find(arg => arg !== undefined) +const firstNonUndefined = (...args: (T | undefined)[]) => args.find(arg => arg !== undefined) const DEFAULT_EXTERNAL_REL_ATTRIBUTE = 'noopener noreferrer' diff --git a/packages/nuxt/src/app/composables/component.ts b/packages/nuxt/src/app/composables/component.ts index 63348c6538a..bceff5e2322 100644 --- a/packages/nuxt/src/app/composables/component.ts +++ b/packages/nuxt/src/app/composables/component.ts @@ -1,8 +1,8 @@ import { defineComponent, getCurrentInstance, reactive, toRefs } from 'vue' import type { DefineComponent } from 'vue' -import { useRoute } from 'vue-router' import { NuxtApp, useNuxtApp } from '../nuxt' import { useAsyncData } from './asyncData' +import { useRoute } from './router' export const NuxtComponentIndicator = '__nuxt_component' diff --git a/packages/nuxt/src/pages/runtime/page.ts b/packages/nuxt/src/pages/runtime/page.ts index 8ed936f3cdb..b5db98e17a3 100644 --- a/packages/nuxt/src/pages/runtime/page.ts +++ b/packages/nuxt/src/pages/runtime/page.ts @@ -1,7 +1,7 @@ import { computed, defineComponent, h, provide, reactive, onMounted, nextTick, Suspense, Transition, KeepAliveProps, TransitionProps } from 'vue' import type { DefineComponent, VNode } from 'vue' -import { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouterView } from 'vue-router' -import type { RouteLocation } from 'vue-router' +import { RouterView } from 'vue-router' +import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteLocation } from 'vue-router' import { generateRouteKey, RouterViewSlotProps, wrapInKeepAlive } from './utils' import { useNuxtApp } from '#app' diff --git a/packages/nuxt/src/pages/runtime/utils.ts b/packages/nuxt/src/pages/runtime/utils.ts index e0700cf4bd9..7785f27d7a6 100644 --- a/packages/nuxt/src/pages/runtime/utils.ts +++ b/packages/nuxt/src/pages/runtime/utils.ts @@ -1,5 +1,5 @@ import { KeepAlive, h } from 'vue' -import { RouterView, RouteLocationMatched, RouteLocationNormalizedLoaded } from 'vue-router' +import type { RouterView, RouteLocationMatched, RouteLocationNormalizedLoaded } from 'vue-router' type InstanceOf = T extends new (...args: any[]) => infer R ? R : never export type RouterViewSlotProps = Parameters['$slots']['default']>[0] From a8635b428324617147bb18c0c5afe8a83119a471 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 14 Oct 2022 09:25:04 +0100 Subject: [PATCH 3/6] test: allow manual check for size changes --- package.json | 1 + test/bundle.test.ts | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 125aeec897a..fdd545219f3 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "test:fixtures:webpack": "NUXT_TELEMETRY_DISABLED=1 TEST_WITH_WEBPACK=1 yarn test:fixtures", "test:types": "yarn run nuxi prepare test/fixtures/basic && cd test/fixtures/basic && npx vue-tsc --noEmit", "test:unit": "JITI_ESM_RESOLVE=1 yarn vitest run --dir packages", + "test:size": "ASSERT_SIZE=1 JITI_ESM_RESOLVE=1 yarn vitest run test/bundle.test.ts", "typecheck": "tsc --noEmit", "version": "yarn && git add yarn.lock" }, diff --git a/test/bundle.test.ts b/test/bundle.test.ts index d003e6cff91..2b7b55f4168 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -9,6 +9,8 @@ const rootDir = fileURLToPath(new URL('..', import.meta.url)) const publicDir = join(rootDir, '.output/public') const serverDir = join(rootDir, '.output/server') +const testSize = !!process.env.ASSERT_SIZE + describe('minimal nuxt application', () => { beforeAll(async () => { await execaCommand('yarn nuxi build', { @@ -18,8 +20,10 @@ describe('minimal nuxt application', () => { it('default client bundle size', async () => { const { files, size } = await measure('**/*.js', publicDir) - expect(size).toBeLessThan(110000) - // expect(size).toMatchInlineSnapshot('106916') + if (testSize) { + expect(size).toBeLessThan(110000) + // expect(size).toMatchInlineSnapshot('106916') + } expect(files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` [ "_nuxt/entry.js", @@ -31,13 +35,17 @@ describe('minimal nuxt application', () => { }) it('default server bundle size', async () => { - const serverBundle = await measure(['**/*.mjs', '!node_modules'], serverDir) - expect(serverBundle.size).toBeLessThan(120000) - // expect(serverBundle.size).toMatchInlineSnapshot('114018') + if (testSize) { + const serverBundle = await measure(['**/*.mjs', '!node_modules'], serverDir) + expect(serverBundle.size).toBeLessThan(120000) + // expect(serverBundle.size).toMatchInlineSnapshot('114018') + } const modules = await measure('node_modules/**/*', serverDir) - expect(modules.size).toBeLessThan(2700000) - // expect(modules.size).toMatchInlineSnapshot('2637251') + if (testSize) { + expect(modules.size).toBeLessThan(2700000) + // expect(modules.size).toMatchInlineSnapshot('2637251') + } const packages = modules.files .filter(m => m.endsWith('package.json')) From d537b3164a849cf52bd18c5066daa8bd89b378fc Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 14 Oct 2022 09:26:41 +0100 Subject: [PATCH 4/6] test: update with new dependencies from `@vueuse/head` --- test/bundle.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/bundle.test.ts b/test/bundle.test.ts index 2b7b55f4168..2d0630f1cb6 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -62,6 +62,7 @@ describe('minimal nuxt application', () => { "@vue/runtime-dom", "@vue/server-renderer", "@vue/shared", + "@vueuse/shared", "buffer-from", "cookie-es", "defu", @@ -83,6 +84,7 @@ describe('minimal nuxt application', () => { "unstorage", "vue", "vue-bundle-renderer", + "vue-demi", ] `) }) From 6f4c402dd031f22cf4741c424e7d0e21160c4cd3 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Oct 2022 17:04:36 +0200 Subject: [PATCH 5/6] add minimal fixture, write stats and use byte size (not disk size) --- test/bundle.test.ts | 58 +++++++++++++--------------- test/fixtures/minimal/app.vue | 3 ++ test/fixtures/minimal/nuxt.config.ts | 1 + test/fixtures/minimal/tsconfig.json | 3 ++ 4 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 test/fixtures/minimal/app.vue create mode 100644 test/fixtures/minimal/nuxt.config.ts create mode 100644 test/fixtures/minimal/tsconfig.json diff --git a/test/bundle.test.ts b/test/bundle.test.ts index 2d0630f1cb6..4250329122a 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -1,30 +1,32 @@ import { fileURLToPath } from 'node:url' import fsp from 'node:fs/promises' -import { beforeAll, describe, expect, it } from 'vitest' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { execaCommand } from 'execa' import { globby } from 'globby' import { join } from 'pathe' -const rootDir = fileURLToPath(new URL('..', import.meta.url)) -const publicDir = join(rootDir, '.output/public') -const serverDir = join(rootDir, '.output/server') +describe('minimal nuxt application', () => { + const rootDir = fileURLToPath(new URL('./fixtures/minimal', import.meta.url)) + const publicDir = join(rootDir, '.output/public') + const serverDir = join(rootDir, '.output/server') -const testSize = !!process.env.ASSERT_SIZE + const stats = { + client: { totalBytes: 0, files: [] as string[] }, + server: { totalBytes: 0, files: [] as string[] } + } -describe('minimal nuxt application', () => { beforeAll(async () => { - await execaCommand('yarn nuxi build', { - cwd: rootDir - }) + await execaCommand(`pnpm nuxi build ${rootDir}`) }, 120 * 1000) + afterAll(async () => { + await fsp.writeFile(join(rootDir, '.output/test-stats.json'), JSON.stringify(stats, null, 2)) + }) + it('default client bundle size', async () => { - const { files, size } = await measure('**/*.js', publicDir) - if (testSize) { - expect(size).toBeLessThan(110000) - // expect(size).toMatchInlineSnapshot('106916') - } - expect(files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` + stats.client = await analyzeSizes('**/*.js', publicDir) + expect(stats.client.totalBytes).toBeLessThan(110000) + expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` [ "_nuxt/entry.js", "_nuxt/error-404.js", @@ -35,17 +37,11 @@ describe('minimal nuxt application', () => { }) it('default server bundle size', async () => { - if (testSize) { - const serverBundle = await measure(['**/*.mjs', '!node_modules'], serverDir) - expect(serverBundle.size).toBeLessThan(120000) - // expect(serverBundle.size).toMatchInlineSnapshot('114018') - } + stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir) + expect(stats.server.totalBytes).toBeLessThan(120000) - const modules = await measure('node_modules/**/*', serverDir) - if (testSize) { - expect(modules.size).toBeLessThan(2700000) - // expect(modules.size).toMatchInlineSnapshot('2637251') - } + const modules = await analyzeSizes('node_modules/**/*', serverDir) + expect(modules.totalBytes).toBeLessThan(2700000) const packages = modules.files .filter(m => m.endsWith('package.json')) @@ -90,12 +86,12 @@ describe('minimal nuxt application', () => { }) }) -async function measure (pattern: string | string[], rootDir: string) { - const files = await globby(pattern, { cwd: rootDir }) - let size = 0 +async function analyzeSizes (pattern: string | string[], rootDir: string) { + const files: string[] = await globby(pattern, { cwd: rootDir }) + let totalBytes = 0 for (const file of files) { - const stats = await fsp.stat(join(rootDir, file)) - size += stats.size + const bytes = Buffer.byteLength(await fsp.readFile(join(rootDir, file))) + totalBytes += bytes } - return { files, size } + return { files, totalBytes } } diff --git a/test/fixtures/minimal/app.vue b/test/fixtures/minimal/app.vue new file mode 100644 index 00000000000..585f495151c --- /dev/null +++ b/test/fixtures/minimal/app.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/minimal/nuxt.config.ts b/test/fixtures/minimal/nuxt.config.ts new file mode 100644 index 00000000000..268da7f8c11 --- /dev/null +++ b/test/fixtures/minimal/nuxt.config.ts @@ -0,0 +1 @@ +export default defineNuxtConfig({}) diff --git a/test/fixtures/minimal/tsconfig.json b/test/fixtures/minimal/tsconfig.json new file mode 100644 index 00000000000..4b34df1571f --- /dev/null +++ b/test/fixtures/minimal/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} From 8fea6868d25e0496fd0ca39d5063a44169a4f1cf Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Wed, 19 Oct 2022 17:18:15 +0200 Subject: [PATCH 6/6] skip windows for now --- test/bundle.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/bundle.test.ts b/test/bundle.test.ts index 4250329122a..fa515f9116e 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -4,8 +4,9 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { execaCommand } from 'execa' import { globby } from 'globby' import { join } from 'pathe' +import { isWindows } from 'std-env' -describe('minimal nuxt application', () => { +describe.skipIf(isWindows)('minimal nuxt application', () => { const rootDir = fileURLToPath(new URL('./fixtures/minimal', import.meta.url)) const publicDir = join(rootDir, '.output/public') const serverDir = join(rootDir, '.output/server')