diff --git a/benchmark-manifest.json b/benchmark-manifest.json index 945f84e53..257f34d93 100644 --- a/benchmark-manifest.json +++ b/benchmark-manifest.json @@ -18,7 +18,11 @@ { "name": "vanilla-extract-devup-ui", "group": "turbopack", "gate": true }, { "name": "tailwind-turbo-devup-ui", "group": "turbopack", "gate": true }, { "name": "vinext-devup-ui", "group": "vite", "gate": true }, - { "name": "devup-ui-collapse", "group": "turbopack", "gate": true } + { "name": "devup-ui-collapse", "group": "turbopack", "gate": true }, + { "name": "devup-ui-large", "group": "webpack" }, + { "name": "devup-ui-large-wasm", "group": "webpack" }, + { "name": "devup-ui-large-turbo", "group": "turbopack" }, + { "name": "devup-ui-large-turbo-wasm", "group": "turbopack" } ], "controls": { "webpack": "tailwind", diff --git a/benchmark/large-app.mjs b/benchmark/large-app.mjs new file mode 100644 index 000000000..874d73332 --- /dev/null +++ b/benchmark/large-app.mjs @@ -0,0 +1,112 @@ +// Writes the generated source of the large-app benchmark fixtures. +// +// The other fixtures are one page, which hides what grows with an app: import +// resolution, barrels, constants read across modules, and the work done once per +// module. This app has ROUTES pages, each rendering COMPONENTS_PER_ROUTE of the +// COMPONENTS components through one barrel, all reading shared tokens. +// +// node ../large-app.mjs (from a fixture directory; files already up to date are left alone) +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' + +const ROUTES = 30 +const COMPONENTS = 300 +const COMPONENTS_PER_ROUTE = 10 +const TONES = 20 + +const pad = (value, width) => String(value).padStart(width, '0') +const component = (index) => `C${pad(index, 3)}` + +function write(path, content) { + const target = join(process.cwd(), 'src', path) + if (existsSync(target) && readFileSync(target, 'utf8') === content) return + mkdirSync(dirname(target), { recursive: true }) + writeFileSync(target, content) +} + +const tones = Array.from( + { length: TONES }, + (_, index) => + ` t${index}: '#${pad(((index * 0x0b3d17) % 0xffffff).toString(16), 6)}',`, +) +write( + 'tokens.ts', + `export const tones = {\n${tones.join('\n')}\n} as const\n\nexport const space = { s: 4, m: 8, l: 16 } as const\n`, +) + +for (let index = 0; index < COMPONENTS; index++) { + const name = component(index) + write( + `components/${name}.tsx`, + `import { Box, css, Flex, styled, Text } from '@devup-ui/react' + +import { space, tones } from '../tokens' + +const Frame = styled('section', { + borderRadius: ${4 + (index % 8)}, + p: space.m, +}) + +export function ${name}({ active }: { active?: boolean }) { + return ( + + + + item ${index} + + + + ) +} +`, + ) +} + +write( + 'components/index.ts', + Array.from( + { length: COMPONENTS }, + (_, index) => `export { ${component(index)} } from './${component(index)}'`, + ).join('\n') + '\n', +) + +const routes = [] +for (let route = 0; route < ROUTES; route++) { + const names = Array.from({ length: COMPONENTS_PER_ROUTE }, (_, offset) => + component((route * COMPONENTS_PER_ROUTE + offset) % COMPONENTS), + ) + const path = `r${pad(route, 2)}` + routes.push(path) + write( + `app/${path}/page.tsx`, + `import { ${names.join(', ')} } from '@/components' + +export default function Page() { + return ( +
+${names.map((name, offset) => ` <${name} active={${offset % 2 === 0}} />`).join('\n')} +
+ ) +} +`, + ) +} + +write( + 'app/page.tsx', + `import { Box } from '@devup-ui/react' + +export default function Page() { + return ( + +${routes.map((path) => ` ${path}`).join('\n')} + + ) +} +`, +) diff --git a/benchmark/next-devup-ui-large-turbo-wasm/.gitignore b/benchmark/next-devup-ui-large-turbo-wasm/.gitignore new file mode 100644 index 000000000..5cf5b19e6 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo-wasm/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/.next/ +/df/ +*.tsbuildinfo +next-env.d.ts + +# written by ../large-app.mjs +/src/app/page.tsx +/src/app/r*/ +/src/components/ +/src/tokens.ts diff --git a/benchmark/next-devup-ui-large-turbo-wasm/next.config.ts b/benchmark/next-devup-ui-large-turbo-wasm/next.config.ts new file mode 100644 index 000000000..db41da922 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo-wasm/next.config.ts @@ -0,0 +1,5 @@ +import { DevupUI } from '@devup-ui/next-plugin' + +const nextConfig = { experimental: { useTypeScriptCli: true } } + +export default DevupUI(nextConfig, { engine: 'wasm' }) diff --git a/benchmark/next-devup-ui-large-turbo-wasm/package.json b/benchmark/next-devup-ui-large-turbo-wasm/package.json new file mode 100644 index 000000000..947c73560 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo-wasm/package.json @@ -0,0 +1,22 @@ +{ + "name": "next-devup-ui-large-turbo-wasm-benchmark", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "build": "node ../large-app.mjs && next build" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1", + "next": "^16.4.6", + "@devup-ui/react": "workspace:^" + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "typescript": "^7.0.2", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0" + } +} diff --git a/benchmark/next-devup-ui-large-turbo-wasm/src/app/layout.tsx b/benchmark/next-devup-ui-large-turbo-wasm/src/app/layout.tsx new file mode 100644 index 000000000..6b8b45180 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo-wasm/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/benchmark/next-devup-ui-large-turbo-wasm/tsconfig.json b/benchmark/next-devup-ui-large-turbo-wasm/tsconfig.json new file mode 100644 index 000000000..a97880733 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo-wasm/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "df/*.d.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/benchmark/next-devup-ui-large-turbo/.gitignore b/benchmark/next-devup-ui-large-turbo/.gitignore new file mode 100644 index 000000000..5cf5b19e6 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/.next/ +/df/ +*.tsbuildinfo +next-env.d.ts + +# written by ../large-app.mjs +/src/app/page.tsx +/src/app/r*/ +/src/components/ +/src/tokens.ts diff --git a/benchmark/next-devup-ui-large-turbo/next.config.ts b/benchmark/next-devup-ui-large-turbo/next.config.ts new file mode 100644 index 000000000..5af9af04e --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo/next.config.ts @@ -0,0 +1,5 @@ +import { DevupUI } from '@devup-ui/next-plugin' + +const nextConfig = { experimental: { useTypeScriptCli: true } } + +export default DevupUI(nextConfig, {}) diff --git a/benchmark/next-devup-ui-large-turbo/package.json b/benchmark/next-devup-ui-large-turbo/package.json new file mode 100644 index 000000000..4ac6aa573 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo/package.json @@ -0,0 +1,22 @@ +{ + "name": "next-devup-ui-large-turbo-benchmark", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "build": "node ../large-app.mjs && next build" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1", + "next": "^16.4.6", + "@devup-ui/react": "workspace:^" + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "typescript": "^7.0.2", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0" + } +} diff --git a/benchmark/next-devup-ui-large-turbo/src/app/layout.tsx b/benchmark/next-devup-ui-large-turbo/src/app/layout.tsx new file mode 100644 index 000000000..6b8b45180 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/benchmark/next-devup-ui-large-turbo/tsconfig.json b/benchmark/next-devup-ui-large-turbo/tsconfig.json new file mode 100644 index 000000000..a97880733 --- /dev/null +++ b/benchmark/next-devup-ui-large-turbo/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "df/*.d.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/benchmark/next-devup-ui-large-wasm/.gitignore b/benchmark/next-devup-ui-large-wasm/.gitignore new file mode 100644 index 000000000..5cf5b19e6 --- /dev/null +++ b/benchmark/next-devup-ui-large-wasm/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/.next/ +/df/ +*.tsbuildinfo +next-env.d.ts + +# written by ../large-app.mjs +/src/app/page.tsx +/src/app/r*/ +/src/components/ +/src/tokens.ts diff --git a/benchmark/next-devup-ui-large-wasm/next.config.ts b/benchmark/next-devup-ui-large-wasm/next.config.ts new file mode 100644 index 000000000..db41da922 --- /dev/null +++ b/benchmark/next-devup-ui-large-wasm/next.config.ts @@ -0,0 +1,5 @@ +import { DevupUI } from '@devup-ui/next-plugin' + +const nextConfig = { experimental: { useTypeScriptCli: true } } + +export default DevupUI(nextConfig, { engine: 'wasm' }) diff --git a/benchmark/next-devup-ui-large-wasm/package.json b/benchmark/next-devup-ui-large-wasm/package.json new file mode 100644 index 000000000..f93b15db9 --- /dev/null +++ b/benchmark/next-devup-ui-large-wasm/package.json @@ -0,0 +1,22 @@ +{ + "name": "next-devup-ui-large-wasm-benchmark", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "build": "node ../large-app.mjs && next build --webpack" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1", + "next": "^16.4.6", + "@devup-ui/react": "workspace:^" + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "typescript": "^7.0.2", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0" + } +} diff --git a/benchmark/next-devup-ui-large-wasm/src/app/layout.tsx b/benchmark/next-devup-ui-large-wasm/src/app/layout.tsx new file mode 100644 index 000000000..6b8b45180 --- /dev/null +++ b/benchmark/next-devup-ui-large-wasm/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/benchmark/next-devup-ui-large-wasm/tsconfig.json b/benchmark/next-devup-ui-large-wasm/tsconfig.json new file mode 100644 index 000000000..a97880733 --- /dev/null +++ b/benchmark/next-devup-ui-large-wasm/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "df/*.d.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/benchmark/next-devup-ui-large/.gitignore b/benchmark/next-devup-ui-large/.gitignore new file mode 100644 index 000000000..5cf5b19e6 --- /dev/null +++ b/benchmark/next-devup-ui-large/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/.next/ +/df/ +*.tsbuildinfo +next-env.d.ts + +# written by ../large-app.mjs +/src/app/page.tsx +/src/app/r*/ +/src/components/ +/src/tokens.ts diff --git a/benchmark/next-devup-ui-large/next.config.ts b/benchmark/next-devup-ui-large/next.config.ts new file mode 100644 index 000000000..5af9af04e --- /dev/null +++ b/benchmark/next-devup-ui-large/next.config.ts @@ -0,0 +1,5 @@ +import { DevupUI } from '@devup-ui/next-plugin' + +const nextConfig = { experimental: { useTypeScriptCli: true } } + +export default DevupUI(nextConfig, {}) diff --git a/benchmark/next-devup-ui-large/package.json b/benchmark/next-devup-ui-large/package.json new file mode 100644 index 000000000..2bf87dcd8 --- /dev/null +++ b/benchmark/next-devup-ui-large/package.json @@ -0,0 +1,22 @@ +{ + "name": "next-devup-ui-large-benchmark", + "version": "0.1.0", + "type": "module", + "private": true, + "scripts": { + "build": "node ../large-app.mjs && next build --webpack" + }, + "dependencies": { + "react": "^19.3.1", + "react-dom": "^19.3.1", + "next": "^16.4.6", + "@devup-ui/react": "workspace:^" + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "typescript": "^7.0.2", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0" + } +} diff --git a/benchmark/next-devup-ui-large/src/app/layout.tsx b/benchmark/next-devup-ui-large/src/app/layout.tsx new file mode 100644 index 000000000..6b8b45180 --- /dev/null +++ b/benchmark/next-devup-ui-large/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/benchmark/next-devup-ui-large/tsconfig.json b/benchmark/next-devup-ui-large/tsconfig.json new file mode 100644 index 000000000..a97880733 --- /dev/null +++ b/benchmark/next-devup-ui-large/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "df/*.d.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": ["node_modules"] +} diff --git a/bun.lock b/bun.lock index bcf47243b..ebe3d52e0 100644 --- a/bun.lock +++ b/bun.lock @@ -221,6 +221,74 @@ "typescript": "^7.0.2", }, }, + "benchmark/next-devup-ui-large": { + "name": "next-devup-ui-large-benchmark", + "version": "0.1.0", + "dependencies": { + "@devup-ui/react": "workspace:^", + "next": "^16.4.6", + "react": "^19.3.1", + "react-dom": "^19.3.1", + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "typescript": "^7.0.2", + }, + }, + "benchmark/next-devup-ui-large-turbo": { + "name": "next-devup-ui-large-turbo-benchmark", + "version": "0.1.0", + "dependencies": { + "@devup-ui/react": "workspace:^", + "next": "^16.4.6", + "react": "^19.3.1", + "react-dom": "^19.3.1", + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "typescript": "^7.0.2", + }, + }, + "benchmark/next-devup-ui-large-turbo-wasm": { + "name": "next-devup-ui-large-turbo-wasm-benchmark", + "version": "0.1.0", + "dependencies": { + "@devup-ui/react": "workspace:^", + "next": "^16.4.6", + "react": "^19.3.1", + "react-dom": "^19.3.1", + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "typescript": "^7.0.2", + }, + }, + "benchmark/next-devup-ui-large-wasm": { + "name": "next-devup-ui-large-wasm-benchmark", + "version": "0.1.0", + "dependencies": { + "@devup-ui/react": "workspace:^", + "next": "^16.4.6", + "react": "^19.3.1", + "react-dom": "^19.3.1", + }, + "devDependencies": { + "@devup-ui/next-plugin": "workspace:^", + "@types/node": "^26.6.6", + "@types/react": "^19.3.0", + "@types/react-dom": "^19.3.0", + "typescript": "^7.0.2", + }, + }, "benchmark/next-devup-ui-single": { "name": "next-devup-ui-single-benchmark", "version": "0.1.0", @@ -2740,6 +2808,14 @@ "next-devup-ui-collapse-benchmark": ["next-devup-ui-collapse-benchmark@workspace:benchmark/next-devup-ui-collapse"], + "next-devup-ui-large-benchmark": ["next-devup-ui-large-benchmark@workspace:benchmark/next-devup-ui-large"], + + "next-devup-ui-large-turbo-benchmark": ["next-devup-ui-large-turbo-benchmark@workspace:benchmark/next-devup-ui-large-turbo"], + + "next-devup-ui-large-turbo-wasm-benchmark": ["next-devup-ui-large-turbo-wasm-benchmark@workspace:benchmark/next-devup-ui-large-turbo-wasm"], + + "next-devup-ui-large-wasm-benchmark": ["next-devup-ui-large-wasm-benchmark@workspace:benchmark/next-devup-ui-large-wasm"], + "next-devup-ui-single-benchmark": ["next-devup-ui-single-benchmark@workspace:benchmark/next-devup-ui-single"], "next-devup-ui-single-turbo-benchmark": ["next-devup-ui-single-turbo-benchmark@workspace:benchmark/next-devup-ui-single-turbo"],