feat: test simple and with-components cases

This commit is contained in:
bholmesdev 2023-02-27 17:04:50 -05:00
parent d8bfeb1553
commit 38e29600ba
22 changed files with 385 additions and 53 deletions

View file

@ -0,0 +1,2 @@
/// <reference path="../../.astro/types.d.ts" />
/// <reference types="astro/client" />

View file

@ -0,0 +1,80 @@
import { fileURLToPath } from 'url';
import { loadFixture } from '../test/test-utils.js';
import { generatePosts } from './scripts/generate-posts.mjs';
import yargs from 'yargs-parser';
import { cyan, bold, dim } from 'kleur/colors';
// Skip nonessential remark / rehype plugins for a fair comparison.
// This includes heading ID generation, syntax highlighting, GFM, and Smartypants.
process.env.ASTRO_CI_PERFORMANCE_RUN = true;
const extByFixture = {
md: '.md',
mdx: '.mdx',
mdoc: '.mdoc',
};
const NUM_POSTS = 1000;
async function benchmark({ fixtures, templates }) {
for (const fixture of fixtures) {
const root = new URL(`./fixtures/${fixture}/`, import.meta.url);
await generatePosts({
postsDir: fileURLToPath(new URL('./src/content/generated/', root)),
numPosts: NUM_POSTS,
ext: extByFixture[fixture],
template: templates[fixture],
});
console.log(`[${fixture}] Generated posts`);
const { build } = await loadFixture({
root,
});
const now = performance.now();
console.log(`[${fixture}] Building...`);
await build();
console.log(cyan(`[${fixture}] Built in ${bold(getTimeStat(now, performance.now()))}.`));
}
}
// Test the build performance for content collections across multiple file types (md, mdx, mdoc)
(async function benchmarkAll() {
try {
const flags = yargs(process.argv.slice(2));
const test = Array.isArray(flags.test)
? flags.test
: typeof flags.test === 'string'
? [flags.test]
: ['simple', 'with-components'];
if (test.includes('simple')) {
console.log(`\n${bold('Simple')} ${dim(`${NUM_POSTS} posts (md, mdx, mdoc)`)}`);
await benchmark({
fixtures: ['md', 'mdx', 'mdoc'],
templates: {
md: 'simple.md',
mdx: 'simple.md',
mdoc: 'simple.md',
},
});
}
if (test.includes('with-components')) {
console.log(`\n${bold('With components')} ${dim(`${NUM_POSTS} posts (mdx, mdoc)`)}`);
await benchmark({
fixtures: ['mdx', 'mdoc'],
templates: {
mdx: 'with-components.mdx',
mdoc: 'with-components.mdoc',
},
});
}
} finally {
process.env.ASTRO_CI_PERFORMANCE_RUN = false;
}
})();
function getTimeStat(timeStart, timeEnd) {
const buildTime = timeEnd - timeStart;
return buildTime < 750 ? `${Math.round(buildTime)}ms` : `${(buildTime / 1000).toFixed(2)}s`;
}

View file

@ -0,0 +1,12 @@
---
import type { CollectionEntry } from 'astro:content';
type Props = {
entry: CollectionEntry<'generated'>;
}
const { entry } = Astro.props as Props;
const { Content } = await entry.render();
---
<Content />

View file

@ -1,2 +1,3 @@
/// <reference path="../../.astro/types.d.ts" />
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View file

@ -1,8 +1,12 @@
---
import { getCollection } from 'astro:content';
import { RenderContent } from '@performance/utils';
import ContentRenderer from '../ContentRenderer.astro';
const posts = await getCollection('generated');
---
<RenderContent {posts} />
<RenderContent
{posts}
renderer={ContentRenderer}
/>

View file

@ -0,0 +1,26 @@
---
import { Title, Aside } from '@performance/utils';
import type { CollectionEntry } from 'astro:content';
type Props = {
entry: CollectionEntry<'generated'>;
}
const { entry } = Astro.props as Props;
const { Content } = await entry.render();
---
{entry.data.type === 'with-components'
? <Content config={{
tags: {
aside: {
render: 'Aside',
attributes: {
type: { type: String },
title: { type: String },
},
}
}
}} components={{ h1: Title, Aside }} />
: <Content />
}

View file

@ -1,2 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="astro/client" />

View file

@ -1,8 +1,12 @@
---
import { getCollection } from 'astro:content';
import { RenderContent } from '@performance/utils';
import ContentRenderer from '../ContentRenderer.astro';
const posts = await getCollection('generated');
---
<RenderContent {posts} />
<RenderContent
{posts}
renderer={ContentRenderer}
/>

View file

@ -0,0 +1,16 @@
---
import { Title } from '@performance/utils';
import type { CollectionEntry } from 'astro:content';
type Props = {
entry: CollectionEntry<'generated'>;
}
const { entry } = Astro.props as Props;
const { Content } = await entry.render();
---
{entry.data.type === 'with-components'
? <Content components={{ h1: Title }} />
: <Content />
}

View file

@ -1,2 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="astro/client" />

View file

@ -1,8 +1,12 @@
---
import { getCollection } from 'astro:content';
import { RenderContent } from '@performance/utils';
import ContentRenderer from '../ContentRenderer.astro';
const posts = await getCollection('generated');
---
<RenderContent {posts} />
<RenderContent
{posts}
renderer={ContentRenderer}
/>

View file

@ -0,0 +1,116 @@
---
// Inspired by the `Aside` component from docs.astro.build
// https://github.com/withastro/docs/blob/main/src/components/Aside.astro
export interface Props {
type?: 'note' | 'tip' | 'caution' | 'danger';
title?: string;
}
const labelByType = {
note: 'Note',
tip: 'Tip',
caution: 'Caution',
danger: 'Danger',
};
const { type = 'note' } = Astro.props as Props;
const title = Astro.props.title ?? labelByType[type] ?? '';
// SVG icon paths based on GitHub Octicons
const icons: Record<NonNullable<Props['type']>, { viewBox: string; d: string }> = {
note: {
viewBox: '0 0 18 18',
d: 'M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5zm1.75-.25a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM3.5 6.25a.75.75 0 01.75-.75h7a.75.75 0 010 1.5h-7a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h4a.75.75 0 000-1.5h-4z',
},
tip: {
viewBox: '0 0 18 18',
d: 'M14 0a8.8 8.8 0 0 0-6 2.6l-.5.4-.9 1H3.3a1.8 1.8 0 0 0-1.5.8L.1 7.6a.8.8 0 0 0 .4 1.1l3.1 1 .2.1 2.4 2.4.1.2 1 3a.8.8 0 0 0 1 .5l2.9-1.7a1.8 1.8 0 0 0 .8-1.5V9.5l1-1 .4-.4A8.8 8.8 0 0 0 16 2v-.1A1.8 1.8 0 0 0 14.2 0h-.1zm-3.5 10.6-.3.2L8 12.3l.5 1.8 2-1.2a.3.3 0 0 0 .1-.2v-2zM3.7 8.1l1.5-2.3.2-.3h-2a.3.3 0 0 0-.3.1l-1.2 2 1.8.5zm5.2-4.5a7.3 7.3 0 0 1 5.2-2.1h.1a.3.3 0 0 1 .3.3v.1a7.3 7.3 0 0 1-2.1 5.2l-.5.4a15.2 15.2 0 0 1-2.5 2L7.1 11 5 9l1.5-2.3a15.3 15.3 0 0 1 2-2.5l.4-.5zM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-8.4 9.6a1.5 1.5 0 1 0-2.2-2.2 7 7 0 0 0-1.1 3 .2.2 0 0 0 .3.3c.6 0 2.2-.4 3-1.1z',
},
caution: {
viewBox: '-1 1 18 18',
d: 'M8.9 1.5C8.7 1.2 8.4 1 8 1s-.7.2-.9.5l-7 12a1 1 0 0 0 0 1c.2.3.6.5 1 .5H15c.4 0 .7-.2.9-.5a1 1 0 0 0 0-1l-7-12zM9 13H7v-2h2v2zm0-3H7V6h2v4z',
},
danger: {
viewBox: '0 1 14 17',
d: 'M5 .3c.9 2.2.5 3.4-.5 4.3C3.5 5.6 2 6.5 1 8c-1.5 2-1.7 6.5 3.5 7.7-2.2-1.2-2.6-4.5-.3-6.6-.6 2 .6 3.3 2 2.8 1.4-.4 2.3.6 2.2 1.7 0 .8-.3 1.4-1 1.8A5.6 5.6 0 0 0 12 10c0-2.9-2.5-3.3-1.3-5.7-1.5.2-2 1.2-1.8 2.8 0 1-1 1.8-2 1.3-.6-.4-.6-1.2 0-1.8C8.2 5.3 8.7 2.5 5 .3z',
},
};
const { viewBox, d } = icons[type];
---
<aside class={`content ${type}`} aria-label={title}>
<p class="title" aria-hidden="true">
<span class="icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox={viewBox} width={16} height={16}>
<path fill-rule="evenodd" d={d}></path>
</svg>
</span>
{title}
</p>
<section>
<slot />
</section>
</aside>
<style>
aside {
--color-base-purple: 269, 79%;
--color-base-teal: 180, 80%;
--color-base-red: 351, 100%;
--color-base-yellow: 41, 100%;
--aside-color-base: var(--color-base-purple);
--aside-color-lightness: 54%;
--aside-accent-color: hsl(var(--aside-color-base), var(--aside-color-lightness));
--aside-text-lightness: 20%;
--aside-text-accent-color: hsl(var(--aside-color-base), var(--aside-text-lightness));
border-inline-start: 4px solid var(--aside-accent-color);
padding: 1rem;
background-color: hsla(var(--aside-color-base), var(--aside-color-lightness), 0.1);
/* Indicates the aside boundaries for forced colors users, transparent is changed to a solid color */
outline: 1px solid transparent;
}
.title {
line-height: 1;
margin-bottom: 0.5rem;
font-size: 0.9rem;
letter-spacing: 0.05em;
font-weight: bold;
text-transform: uppercase;
color: var(--aside-text-accent-color);
}
.icon svg {
width: 1.5em;
height: 1.5em;
vertical-align: middle;
fill: currentcolor;
}
aside :global(a),
aside :global(a > code:not([class*='language'])) {
color: var(--aside-text-accent-color);
}
aside :global(pre) {
margin-left: 0;
margin-right: 0;
}
.tip {
--aside-color-lightness: 42%;
--aside-color-base: var(--color-base-teal);
}
.caution {
--aside-color-lightness: 59%;
--aside-color-base: var(--color-base-yellow);
}
.danger {
--aside-color-lightness: 54%;
--aside-color-base: var(--color-base-red);
}
</style>

View file

@ -1,9 +1,10 @@
---
type Props = {
posts: any[];
renderer: any;
}
const { posts } = Astro.props;
const { posts, renderer: ContentRenderer } = Astro.props;
---
<!DOCTYPE html>
@ -16,13 +17,12 @@ const { posts } = Astro.props;
</head>
<body>
{
posts.map(async (entry) => {
const { Content } = await entry.render();
posts.map((entry) => {
return (
<div>
<h1>{entry.data.title}</h1>
<p>{entry.data.description}</p>
<Content />
<ContentRenderer {entry} />
</div>
);
})

View file

@ -0,0 +1,8 @@
<h1><slot /></h1>
<style>
h1 {
color: red;
text-transform: uppercase;
}
</style>

View file

@ -1,2 +1,4 @@
// @ts-ignore
// @ts-nocheck
export { default as RenderContent } from './RenderContent.astro';
export { default as Aside } from './Aside.astro';
export { default as Title } from './Title.astro';

View file

@ -4,20 +4,16 @@
"description": "",
"main": "index.js",
"scripts": {
"write": "run-p write:*",
"write:md": "node scripts/write-posts.mjs fixtures/md/src/content/generated 1000 md",
"write:mdx": "node scripts/write-posts.mjs fixtures/mdx/src/content/generated 1000 mdx",
"write:mdoc": "node scripts/write-posts.mjs fixtures/mdoc/src/content/generated 1000 mdoc",
"build": "cross-env ASTRO_CI_PERFORMANCE_RUN=true run-s build:*",
"build:md": "cd fixtures/md && run-s build",
"build:mdx": "cd fixtures/mdx && run-s build",
"build:mdoc": "cd fixtures/mdoc && run-s build"
"benchmark": "node ./content-benchmark.mjs"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/yargs-parser": "^21.0.0",
"cross-env": "^7.0.3",
"npm-run-all": "^4.1.5"
"kleur": "^4.1.5",
"npm-run-all": "^4.1.5",
"yargs-parser": "^21.0.1"
}
}

View file

@ -0,0 +1,16 @@
import { generatePosts } from './generate-posts.mjs';
(async () => {
const postsDir = process.argv[2];
const numPosts =
typeof process.argv[3] === 'string' && process.argv[3].length > 0
? Number(process.argv[3])
: undefined;
const ext = process.argv[4];
const template = process.argv[5];
await generatePosts({ postsDir, numPosts, ext, template });
console.log(`${numPosts} ${ext} posts written to ${JSON.stringify(postsDir)} 🚀`);
})();

View file

@ -0,0 +1,30 @@
import fs from 'fs';
import path from 'path';
const NUM_POSTS = 10;
const POSTS_DIR = './src/content/posts.generated';
const EXT = '.md';
const TEMPLATE = 'simple.md';
export async function generatePosts({
postsDir = POSTS_DIR,
numPosts = NUM_POSTS,
ext = EXT,
template = TEMPLATE,
}) {
if (fs.existsSync(postsDir)) {
const files = await fs.promises.readdir(postsDir);
await Promise.all(files.map((file) => fs.promises.unlink(path.join(postsDir, file))));
} else {
await fs.promises.mkdir(postsDir, { recursive: true });
}
await Promise.all(
Array.from(Array(numPosts).keys()).map((idx) => {
return fs.promises.writeFile(
`${postsDir}/post-${idx}${ext.startsWith('.') ? ext : `.${ext}`}`,
fs.readFileSync(new URL(`./templates/${template}`, import.meta.url), 'utf8')
);
})
);
}

View file

@ -1,38 +1,8 @@
import fs from 'fs';
import path from 'path';
const NUM_POSTS = 10;
const POSTS_DIR = './src/content/posts.generated';
const EXT = '.md';
(async function writePosts() {
const postsDir = process.argv[2] ?? POSTS_DIR;
const numPosts = Number(process.argv[3]) ?? NUM_POSTS;
const ext = process.argv[4] ?? EXT;
if (fs.existsSync(postsDir)) {
const files = await fs.promises.readdir(postsDir);
await Promise.all(files.map((file) => fs.promises.unlink(path.join(postsDir, file))));
} else {
await fs.promises.mkdir(postsDir, { recursive: true });
}
await Promise.all(
Array.from(Array(numPosts).keys()).map((idx) => {
return fs.promises.writeFile(
`${postsDir}/post-${idx}${ext.startsWith('.') ? ext : `.${ext}`}`,
toMdContents(idx)
);
})
);
console.log(`${numPosts} ${ext} posts written to ${JSON.stringify(postsDir)} 🚀`);
})();
const toMdContents = (idx) => `---
title: Example ${idx}
---
title: Simple
---
# Post ${idx}
# Simple post
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur interdum quam vitae est dapibus auctor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus id purus vel ante interdum eleifend non sed magna. Nullam aliquet metus eget nunc pretium, ac malesuada elit ultricies. Quisque fermentum tellus sed risus tristique tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas eleifend odio sed tortor rhoncus venenatis. Maecenas dignissim convallis sem et sagittis. Aliquam facilisis auctor consectetur. Morbi vulputate fermentum lobortis. Aenean luctus risus erat, sit amet imperdiet lectus tempor et.
@ -71,4 +41,3 @@ Vestibulum sit amet lorem arcu. Integer sed nisl ut turpis dapibus mollis sit am
Curabitur quis mi ac massa hendrerit ornare id eget velit. Nulla dui lacus, hendrerit et fringilla sed, eleifend ut erat. Nunc ut fringilla ex, sit amet fringilla libero. Maecenas non ullamcorper orci. Duis posuere erat et urna rhoncus iaculis. Proin pellentesque porttitor nulla, non blandit ante semper vitae. Phasellus ut augue venenatis, tempus purus eu, efficitur massa. Etiam vel egestas tellus, ac pharetra lectus. Aliquam non commodo turpis. Quisque pharetra nunc et mauris bibendum, id vestibulum tellus fringilla. Nullam enim massa, porta id nisi at, accumsan sollicitudin elit. Morbi auctor lectus vitae orci cursus, et hendrerit odio accumsan. Pellentesque quis libero auctor, tempor dolor tempor, finibus arcu. Aliquam non cursus ex. Aliquam quis lacus ut purus pellentesque ultrices in a augue.
Morbi nunc diam, egestas sed condimentum a, interdum suscipit ligula. Morbi interdum dignissim imperdiet. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris egestas, nulla nec feugiat porttitor, ex magna sodales nisl, ac volutpat tortor mauris vitae nibh. Cras cursus dignissim pretium. Nunc faucibus dui at lectus pellentesque vehicula. Maecenas tincidunt, libero quis hendrerit aliquet, tortor leo iaculis enim, sit amet ullamcorper tellus risus a orci. Donec dignissim metus in nulla eleifend molestie. Nunc at turpis et sem laoreet rutrum. Nulla facilisi. Sed luctus nisi sed egestas cursus.
`;

View file

@ -0,0 +1,25 @@
---
type: with-components
title: Post with components
---
# This should be rendered with a title component
{% aside type="tip" %}
This is a tip component
{% /aside %}
{% aside type="tip" %}
This is a tip component
{% /aside %}
{% aside type="tip" %}
This is a tip component
{% /aside %}
{% aside type="tip" %}
This is a tip component
{% /aside %}
{% aside type="tip" %}
This is a tip component
{% /aside %}
{% aside type="tip" %}
This is a tip component
{% /aside %}

View file

@ -0,0 +1,15 @@
---
type: with-components
title: Post with components
---
import { Aside } from '@performance/utils';
# This should be rendered with a title component
<Aside type="tip">This is a tip component</Aside>
<Aside type="tip">This is a tip component</Aside>
<Aside type="tip">This is a tip component</Aside>
<Aside type="tip">This is a tip component</Aside>
<Aside type="tip">This is a tip component</Aside>
<Aside type="tip">This is a tip component</Aside>

View file

@ -1134,11 +1134,17 @@ importers:
packages/astro/performance:
specifiers:
'@types/yargs-parser': ^21.0.0
cross-env: ^7.0.3
kleur: ^4.1.5
npm-run-all: ^4.1.5
yargs-parser: ^21.0.1
devDependencies:
'@types/yargs-parser': 21.0.0
cross-env: 7.0.3
kleur: 4.1.5
npm-run-all: 4.1.5
yargs-parser: 21.1.1
packages/astro/performance/fixtures/md:
specifiers: