diff options
author | 2025-06-05 14:25:23 +0000 | |
---|---|---|
committer | 2025-06-05 14:25:23 +0000 | |
commit | e586d7d704d475afe3373a1de6ae20d504f79d6d (patch) | |
tree | 7e3fa24807cebd48a86bd40f866d792181191ee9 /benchmark/make-project | |
download | astro-e586d7d704d475afe3373a1de6ae20d504f79d6d.tar.gz astro-e586d7d704d475afe3373a1de6ae20d504f79d6d.tar.zst astro-e586d7d704d475afe3373a1de6ae20d504f79d6d.zip |
Sync from a8e1c0a7402940e0fc5beef669522b315052df1blatest
Diffstat (limited to 'benchmark/make-project')
-rw-r--r-- | benchmark/make-project/README.md | 7 | ||||
-rw-r--r-- | benchmark/make-project/_template.js | 9 | ||||
-rw-r--r-- | benchmark/make-project/_util.js | 12 | ||||
-rw-r--r-- | benchmark/make-project/image.jpg | bin | 0 -> 174008 bytes | |||
-rw-r--r-- | benchmark/make-project/markdown-cc1.js | 64 | ||||
-rw-r--r-- | benchmark/make-project/markdown-cc2.js | 76 | ||||
-rw-r--r-- | benchmark/make-project/mdx-cc1.js | 67 | ||||
-rw-r--r-- | benchmark/make-project/mdx-cc2.js | 80 | ||||
-rw-r--r-- | benchmark/make-project/memory-default.js | 82 | ||||
-rw-r--r-- | benchmark/make-project/render-bench.js | 132 | ||||
-rw-r--r-- | benchmark/make-project/render-default.js | 132 | ||||
-rw-r--r-- | benchmark/make-project/server-stress-default.js | 62 |
12 files changed, 723 insertions, 0 deletions
diff --git a/benchmark/make-project/README.md b/benchmark/make-project/README.md new file mode 100644 index 000000000..9d1a421c9 --- /dev/null +++ b/benchmark/make-project/README.md @@ -0,0 +1,7 @@ +# make-project + +This `make-project` folder contains different files to programmatically create a new Astro project. They are created inside the `projects` folder and are gitignored. These projects are used by benchmarks for testing. + +Each benchmark can specify the default project to run in its `defaultProject` export, but it can be overridden if `--project <project-name>` is passed through the CLI. + +You can duplicate `_template.js` to start a new project script. All shared utilities are kept in `_util.js`. diff --git a/benchmark/make-project/_template.js b/benchmark/make-project/_template.js new file mode 100644 index 000000000..a99c52428 --- /dev/null +++ b/benchmark/make-project/_template.js @@ -0,0 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * Create a new project in the `projectDir` directory. Make sure to clean up the + * previous artifacts here before generating files. + * @param {URL} projectDir + */ +// biome-ignore lint/correctness/noUnusedVariables: parameters here are template placeholders +// biome-ignore lint/correctness/noUnusedFunctionParameters: (same as above) +export async function run(projectDir) {} diff --git a/benchmark/make-project/_util.js b/benchmark/make-project/_util.js new file mode 100644 index 000000000..65c91dbf3 --- /dev/null +++ b/benchmark/make-project/_util.js @@ -0,0 +1,12 @@ +export const loremIpsum = + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; + +export const loremIpsumHtml = loremIpsum + .replace(/Lorem/g, '<strong>Lorem</strong>') + .replace(/Ipsum/g, '<em>Ipsum</em>') + .replace(/dummy/g, '<span>dummy</span>'); + +export const loremIpsumMd = loremIpsum + .replace(/Lorem/g, '**Lorem**') + .replace(/Ipsum/g, '_Ipsum_') + .replace(/dummy/g, '`dummy`'); diff --git a/benchmark/make-project/image.jpg b/benchmark/make-project/image.jpg Binary files differnew file mode 100644 index 000000000..80b8ea67b --- /dev/null +++ b/benchmark/make-project/image.jpg diff --git a/benchmark/make-project/markdown-cc1.js b/benchmark/make-project/markdown-cc1.js new file mode 100644 index 000000000..1e3aaa517 --- /dev/null +++ b/benchmark/make-project/markdown-cc1.js @@ -0,0 +1,64 @@ +import fs from 'node:fs/promises'; +import { loremIpsumMd } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content/blog', projectDir), { recursive: true }); + await fs.copyFile( + new URL('./image.jpg', import.meta.url), + new URL('./src/image.jpg', projectDir), + ); + + const promises = []; + + for (let i = 0; i < 10000; i++) { + const content = `\ +# Article ${i} + +${loremIpsumMd} + + + + +`; + promises.push( + fs.writeFile(new URL(`./src/content/blog/article-${i}.md`, projectDir), content, 'utf-8'), + ); + } + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await entry.render(); +--- +<h1>{entry.data.title}</h1> +<Content /> +`, + 'utf-8', + ); + + await Promise.all(promises); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; + +export default defineConfig({ +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/markdown-cc2.js b/benchmark/make-project/markdown-cc2.js new file mode 100644 index 000000000..ba60813c0 --- /dev/null +++ b/benchmark/make-project/markdown-cc2.js @@ -0,0 +1,76 @@ +import fs from 'node:fs/promises'; +import { loremIpsumMd } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./data/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content', projectDir), { recursive: true }); + await fs.copyFile(new URL('./image.jpg', import.meta.url), new URL('./image.jpg', projectDir)); + + const promises = []; + + for (let i = 0; i < 10000; i++) { + const content = `\ +# Article ${i} + +${loremIpsumMd} + + + +`; + promises.push( + fs.writeFile(new URL(`./data/blog/article-${i}.md`, projectDir), content, 'utf-8'), + ); + } + + await fs.writeFile( + new URL(`./src/content/config.ts`, projectDir), + /*ts */ ` + import { defineCollection, z } from 'astro:content'; + import { glob } from 'astro/loaders'; + + const blog = defineCollection({ + loader: glob({ pattern: '*', base: './data/blog' }), + }); + + export const collections = { blog } + + `, + ); + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection, render } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.id }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await render(entry); + +--- +<h1>{entry.data.title}</h1> +<Content /> +`, + 'utf-8', + ); + + await Promise.all(promises); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; + +export default defineConfig({});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/mdx-cc1.js b/benchmark/make-project/mdx-cc1.js new file mode 100644 index 000000000..a948ce194 --- /dev/null +++ b/benchmark/make-project/mdx-cc1.js @@ -0,0 +1,67 @@ +import fs from 'node:fs/promises'; +import { loremIpsumMd } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content/blog', projectDir), { recursive: true }); + await fs.copyFile( + new URL('./image.jpg', import.meta.url), + new URL('./src/image.jpg', projectDir), + ); + + const promises = []; + + for (let i = 0; i < 10000; i++) { + const content = `\ +# Article ${i} + +${loremIpsumMd} + + + + +`; + promises.push( + fs.writeFile(new URL(`./src/content/blog/article-${i}.mdx`, projectDir), content, 'utf-8'), + ); + } + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await entry.render(); +--- +<h1>{entry.data.title}</h1> +<Content /> +`, + 'utf-8', + ); + + await Promise.all(promises); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; + +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()], +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/mdx-cc2.js b/benchmark/make-project/mdx-cc2.js new file mode 100644 index 000000000..f50b63c9e --- /dev/null +++ b/benchmark/make-project/mdx-cc2.js @@ -0,0 +1,80 @@ +import fs from 'node:fs/promises'; +import { loremIpsumMd } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./data/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content', projectDir), { recursive: true }); + await fs.copyFile(new URL('./image.jpg', import.meta.url), new URL('./image.jpg', projectDir)); + + const promises = []; + + for (let i = 0; i < 10000; i++) { + const content = `\ +# Article ${i} + +${loremIpsumMd} + + + +`; + promises.push( + fs.writeFile(new URL(`./data/blog/article-${i}.mdx`, projectDir), content, 'utf-8'), + ); + } + + await fs.writeFile( + new URL(`./src/content/config.ts`, projectDir), + /*ts */ ` + import { defineCollection, z } from 'astro:content'; + import { glob } from 'astro/loaders'; + + const blog = defineCollection({ + loader: glob({ pattern: '*', base: './data/blog' }), + }); + + export const collections = { blog } + + `, + ); + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection, render } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.id }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await render(entry); + +--- +<h1>{entry.data.title}</h1> +<Content /> +`, + 'utf-8', + ); + + await Promise.all(promises); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; + +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()], +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/memory-default.js b/benchmark/make-project/memory-default.js new file mode 100644 index 000000000..1087c3d4a --- /dev/null +++ b/benchmark/make-project/memory-default.js @@ -0,0 +1,82 @@ +import fs from 'node:fs/promises'; +import { loremIpsum } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages/blog', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/content/blog', projectDir), { recursive: true }); + + const promises = []; + + for (let i = 0; i < 100; i++) { + const content = `\ +--- +const i = ${i}; +--- + +<span>{i}</span> +`; + promises.push( + fs.writeFile(new URL(`./src/pages/page-${i}.astro`, projectDir), content, 'utf-8'), + ); + } + + for (let i = 0; i < 100; i++) { + const content = `\ +# Article ${i} + +${loremIpsum} +`; + promises.push( + fs.writeFile(new URL(`./src/content/blog/article-${i}.md`, projectDir), content, 'utf-8'), + ); + } + + for (let i = 0; i < 100; i++) { + const content = `\ +# Post ${i} + +${loremIpsum} +`; + promises.push( + fs.writeFile(new URL(`./src/content/blog/post-${i}.mdx`, projectDir), content, 'utf-8'), + ); + } + + await fs.writeFile( + new URL(`./src/pages/blog/[...slug].astro`, projectDir), + `\ +--- +import { getCollection } from 'astro:content'; +export async function getStaticPaths() { + const blogEntries = await getCollection('blog'); + return blogEntries.map(entry => ({ + params: { slug: entry.slug }, props: { entry }, + })); +} +const { entry } = Astro.props; +const { Content } = await entry.render(); +--- +<h1>{entry.data.title}</h1> +<Content /> +`, + 'utf-8', + ); + + await Promise.all(promises); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()], +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/render-bench.js b/benchmark/make-project/render-bench.js new file mode 100644 index 000000000..e2964fbd2 --- /dev/null +++ b/benchmark/make-project/render-bench.js @@ -0,0 +1,132 @@ +import fs from 'node:fs/promises'; +import { loremIpsumHtml, loremIpsumMd } from './_util.js'; + +// Map of files to be generated and tested for rendering. +// Ideally each content should be similar for comparison. +const renderFiles = { + 'components/ListItem.astro': `\ +--- +const { className, item, attrs } = Astro.props; +const nested = item !== 0; +--- + <li class={className}> + <a + href={item} + aria-current={item === 0} + class:list={[{ large: !nested }, className]} + {...attrs} + > + <span>{item}</span> + </a> + </li> + `, + 'components/Sublist.astro': `\ +--- +import ListItem from '../components/ListItem.astro'; +const { items } = Astro.props; +const className = "text-red-500"; +const style = { color: "red" }; +--- +<ul style={style}> +{items.map((item) => ( + <ListItem className={className} item={item} attrs={{}} /> +))} +</ul> + `, + 'pages/astro.astro': `\ +--- +const className = "text-red-500"; +const style = { color: "red" }; +const items = Array.from({ length: 10000 }, (_, i) => ({i})); +--- +<html> + <head> + <title>My Site</title> + </head> + <body> + <h1 class={className + ' text-lg'}>List</h1> + <ul style={style}> + {items.map((item) => ( + <li class={className}> + <a + href={item.i} + aria-current={item.i === 0} + class:list={[{ large: item.i === 0 }, className]} + {...({})} + > + <span>{item.i}</span> + </a> + </li> + ))} + </ul> + ${Array.from({ length: 1000 }) + .map(() => `<p>${loremIpsumHtml}</p>`) + .join('\n')} + </body> +</html>`, + 'pages/md.md': `\ +# List + +${Array.from({ length: 1000 }, (_, i) => i) + .map((v) => `- ${v}`) + .join('\n')} + +${Array.from({ length: 1000 }) + .map(() => loremIpsumMd) + .join('\n\n')} +`, + 'pages/mdx.mdx': `\ +export const className = "text-red-500"; +export const style = { color: "red" }; +export const items = Array.from({ length: 1000 }, (_, i) => i); + +# List + +<ul style={style}> + {items.map((item) => ( + <li class={className}>{item}</li> + ))} +</ul> + +${Array.from({ length: 1000 }) + .map(() => loremIpsumMd) + .join('\n\n')} +`, +}; + +export const renderPages = []; +for (const file of Object.keys(renderFiles)) { + if (file.startsWith('pages/')) { + renderPages.push(file.replace('pages/', '')); + } +} + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/components', projectDir), { recursive: true }); + + await Promise.all( + Object.entries(renderFiles).map(([name, content]) => { + return fs.writeFile(new URL(`./src/${name}`, projectDir), content, 'utf-8'); + }), + ); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import adapter from '@benchmark/adapter'; +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()], + output: 'server', + adapter: adapter(), +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/render-default.js b/benchmark/make-project/render-default.js new file mode 100644 index 000000000..7ea54b936 --- /dev/null +++ b/benchmark/make-project/render-default.js @@ -0,0 +1,132 @@ +import fs from 'node:fs/promises'; +import { loremIpsumHtml, loremIpsumMd } from './_util.js'; + +// Map of files to be generated and tested for rendering. +// Ideally each content should be similar for comparison. +const renderFiles = { + 'components/ListItem.astro': `\ +--- +const { className, item, attrs } = Astro.props; +const nested = item !== 0; +--- + <li class={className}> + <a + href={item} + aria-current={item === 0} + class:list={[{ large: !nested }, className]} + {...attrs} + > + <span>{item}</span> + </a> + </li> + `, + 'components/Sublist.astro': `\ +--- +import ListItem from '../components/ListItem.astro'; +const { items } = Astro.props; +const className = "text-red-500"; +const style = { color: "red" }; +--- +<ul style={style}> +{items.map((item) => ( + <ListItem className={className} item={item} attrs={{}} /> +))} +</ul> + `, + 'pages/astro.astro': `\ +--- +const className = "text-red-500"; +const style = { color: "red" }; +const items = Array.from({ length: 10000 }, (_, i) => ({i})); +--- +<html> + <head> + <title>My Site</title> + </head> + <body> + <h1 class={className + ' text-lg'}>List</h1> + <ul style={style}> + {items.map((item) => ( + <li class={className}> + <a + href={item.i} + aria-current={item.i === 0} + class:list={[{ large: item.i === 0 }, className]} + {...({})} + > + <span>{item.i}</span> + </a> + </li> + ))} + </ul> + ${Array.from({ length: 1000 }) + .map(() => `<p>${loremIpsumHtml}</p>`) + .join('\n')} + </body> +</html>`, + 'pages/md.md': `\ +# List + +${Array.from({ length: 1000 }, (_, i) => i) + .map((v) => `- ${v}`) + .join('\n')} + +${Array.from({ length: 1000 }) + .map(() => loremIpsumMd) + .join('\n\n')} +`, + 'pages/mdx.mdx': `\ +export const className = "text-red-500"; +export const style = { color: "red" }; +export const items = Array.from({ length: 1000 }, (_, i) => i); + +# List + +<ul style={style}> + {items.map((item) => ( + <li class={className}>{item}</li> + ))} +</ul> + +${Array.from({ length: 1000 }) + .map(() => loremIpsumMd) + .join('\n\n')} +`, +}; + +export const renderPages = []; +for (const file of Object.keys(renderFiles)) { + if (file.startsWith('pages/')) { + renderPages.push(file.replace('pages/', '')); + } +} + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/components', projectDir), { recursive: true }); + + await Promise.all( + Object.entries(renderFiles).map(([name, content]) => { + return fs.writeFile(new URL(`./src/${name}`, projectDir), content, 'utf-8'); + }), + ); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import timer from '@benchmark/timer'; +import mdx from '@astrojs/mdx'; + +export default defineConfig({ + integrations: [mdx()], + output: 'server', + adapter: timer(), +});`, + 'utf-8', + ); +} diff --git a/benchmark/make-project/server-stress-default.js b/benchmark/make-project/server-stress-default.js new file mode 100644 index 000000000..1724f8f82 --- /dev/null +++ b/benchmark/make-project/server-stress-default.js @@ -0,0 +1,62 @@ +import fs from 'node:fs/promises'; +import { loremIpsum } from './_util.js'; + +/** + * @param {URL} projectDir + */ +export async function run(projectDir) { + await fs.rm(projectDir, { recursive: true, force: true }); + await fs.mkdir(new URL('./src/pages', projectDir), { recursive: true }); + await fs.mkdir(new URL('./src/components', projectDir), { recursive: true }); + + await fs.writeFile( + new URL('./src/pages/index.astro', projectDir), + `\ +--- +import Paragraph from '../components/Paragraph.astro' +const content = "${loremIpsum}" +--- + +<html lang="en"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + <meta name="generator" content={Astro.generator} /> + <title>Astro</title> + </head> + <body> + <h1>Astro</h1> + <div> + ${Array.from({ length: 100 }) + .map(() => '<p>{content}</p>') + .join('\n')} + </div> + <div> + ${Array.from({ length: 50 }) + .map((_, i) => '<Paragraph num={' + i + '} str={content} />') + .join('\n')} + </div> + </body> +</html>`, + 'utf-8', + ); + + await fs.writeFile( + new URL('./src/components/Paragraph.astro', projectDir), + `<div>{Astro.props.num} {Astro.props.str}</div>`, + 'utf-8', + ); + + await fs.writeFile( + new URL('./astro.config.js', projectDir), + `\ +import { defineConfig } from 'astro/config'; +import nodejs from '@astrojs/node'; + +export default defineConfig({ + output: 'server', + adapter: nodejs({ mode: 'standalone' }), +});`, + 'utf-8', + ); +} |