[Content collections] Load MDX hoisted scripts in dev (#6035)
* chore: script, rename delayed -> propagated * fix: consistent propagatedAssets flag * feat: inject those scripts in dev! * test: scripts included in dev and build * chore: add TODO for prod build fix * chore: changeset
This commit is contained in:
parent
9bb0bfab13
commit
b4432cd6b6
21 changed files with 264 additions and 115 deletions
5
.changeset/eight-kids-attack.md
Normal file
5
.changeset/eight-kids-attack.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fix: Astro component scripts now load in development when using MDX + Content Collections
|
|
@ -1,8 +1,9 @@
|
|||
export const contentFileExts = ['.md', '.mdx'];
|
||||
export const DELAYED_ASSET_FLAG = 'astroAssetSsr';
|
||||
export const PROPAGATED_ASSET_FLAG = 'astroPropagatedAssets';
|
||||
export const CONTENT_FLAG = 'astroContent';
|
||||
export const VIRTUAL_MODULE_ID = 'astro:content';
|
||||
export const LINKS_PLACEHOLDER = '@@ASTRO-LINKS@@';
|
||||
export const STYLES_PLACEHOLDER = '@@ASTRO-STYLES@@';
|
||||
export const SCRIPTS_PLACEHOLDER = '@@ASTRO-SCRIPTS@@';
|
||||
|
||||
export const CONTENT_TYPES_FILE = 'types.d.ts';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
export { createContentTypesGenerator } from './types-generator.js';
|
||||
export { contentObservable, getContentPaths, getDotAstroTypeReference } from './utils.js';
|
||||
export {
|
||||
astroBundleDelayedAssetPlugin,
|
||||
astroDelayedAssetPlugin,
|
||||
astroContentProdBundlePlugin,
|
||||
astroContentAssetPropagationPlugin,
|
||||
} from './vite-plugin-content-assets.js';
|
||||
export { astroContentServerPlugin } from './vite-plugin-content-server.js';
|
||||
export { astroContentVirtualModPlugin } from './vite-plugin-content-virtual-mod.js';
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
createHeadAndContent,
|
||||
renderComponent,
|
||||
renderStyleElement,
|
||||
renderScriptElement,
|
||||
renderTemplate,
|
||||
renderUniqueStylesheet,
|
||||
unescapeHTML,
|
||||
|
@ -127,7 +128,8 @@ async function render({
|
|||
const Content = createComponent({
|
||||
factory(result, props, slots) {
|
||||
let styles = '',
|
||||
links = '';
|
||||
links = '',
|
||||
scripts = '';
|
||||
if (Array.isArray(mod?.collectedStyles)) {
|
||||
styles = mod.collectedStyles.map((style: any) => renderStyleElement(style)).join('');
|
||||
}
|
||||
|
@ -140,9 +142,12 @@ async function render({
|
|||
})
|
||||
.join('');
|
||||
}
|
||||
if (Array.isArray(mod?.collectedScripts)) {
|
||||
scripts = mod.collectedScripts.map((script: any) => renderScriptElement(script)).join('');
|
||||
}
|
||||
|
||||
return createHeadAndContent(
|
||||
unescapeHTML(styles + links) as any,
|
||||
unescapeHTML(styles + links + scripts) as any,
|
||||
renderTemplate`${renderComponent(result, 'Content', mod.Content, props, slots)}`
|
||||
);
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@ const collectionToEntryMap = createCollectionToGlobResultMap({
|
|||
});
|
||||
|
||||
const renderEntryGlob = import.meta.glob('@@RENDER_ENTRY_GLOB_PATH@@', {
|
||||
query: { astroAssetSsr: true },
|
||||
query: { astroPropagatedAssets: true },
|
||||
});
|
||||
const collectionToRenderEntryMap = createCollectionToGlobResultMap({
|
||||
globResult: renderEntryGlob,
|
||||
|
|
|
@ -5,25 +5,27 @@ import { BuildInternals, getPageDataByViteID } from '../core/build/internal.js';
|
|||
import type { ModuleLoader } from '../core/module-loader/loader.js';
|
||||
import { createViteLoader } from '../core/module-loader/vite.js';
|
||||
import { getStylesForURL } from '../core/render/dev/css.js';
|
||||
import { getScriptsForURL } from '../core/render/dev/scripts.js';
|
||||
import {
|
||||
contentFileExts,
|
||||
DELAYED_ASSET_FLAG,
|
||||
PROPAGATED_ASSET_FLAG,
|
||||
LINKS_PLACEHOLDER,
|
||||
SCRIPTS_PLACEHOLDER,
|
||||
STYLES_PLACEHOLDER,
|
||||
} from './consts.js';
|
||||
|
||||
function isDelayedAsset(viteId: string): boolean {
|
||||
function isPropagatedAsset(viteId: string): boolean {
|
||||
const url = new URL(viteId, 'file://');
|
||||
return (
|
||||
url.searchParams.has(DELAYED_ASSET_FLAG) &&
|
||||
url.searchParams.has(PROPAGATED_ASSET_FLAG) &&
|
||||
contentFileExts.some((ext) => url.pathname.endsWith(ext))
|
||||
);
|
||||
}
|
||||
|
||||
export function astroDelayedAssetPlugin({ mode }: { mode: string }): Plugin {
|
||||
export function astroContentAssetPropagationPlugin({ mode }: { mode: string }): Plugin {
|
||||
let devModuleLoader: ModuleLoader;
|
||||
return {
|
||||
name: 'astro-delayed-asset-plugin',
|
||||
name: 'astro:content-asset-propagation',
|
||||
enforce: 'pre',
|
||||
configureServer(server) {
|
||||
if (mode === 'dev') {
|
||||
|
@ -31,19 +33,20 @@ export function astroDelayedAssetPlugin({ mode }: { mode: string }): Plugin {
|
|||
}
|
||||
},
|
||||
load(id) {
|
||||
if (isDelayedAsset(id)) {
|
||||
if (isPropagatedAsset(id)) {
|
||||
const basePath = id.split('?')[0];
|
||||
const code = `
|
||||
export { Content, getHeadings, frontmatter } from ${JSON.stringify(basePath)};
|
||||
export const collectedLinks = ${JSON.stringify(LINKS_PLACEHOLDER)};
|
||||
export const collectedStyles = ${JSON.stringify(STYLES_PLACEHOLDER)};
|
||||
export const collectedScripts = ${JSON.stringify(SCRIPTS_PLACEHOLDER)};
|
||||
`;
|
||||
return { code };
|
||||
}
|
||||
},
|
||||
async transform(code, id, options) {
|
||||
if (!options?.ssr) return;
|
||||
if (devModuleLoader && isDelayedAsset(id)) {
|
||||
if (devModuleLoader && isPropagatedAsset(id)) {
|
||||
const basePath = id.split('?')[0];
|
||||
if (!devModuleLoader.getModuleById(basePath)?.ssrModule) {
|
||||
await devModuleLoader.import(basePath);
|
||||
|
@ -54,23 +57,22 @@ export function astroDelayedAssetPlugin({ mode }: { mode: string }): Plugin {
|
|||
'development'
|
||||
);
|
||||
|
||||
const hoistedScripts = await getScriptsForURL(pathToFileURL(basePath), devModuleLoader);
|
||||
|
||||
return {
|
||||
code: code
|
||||
.replace(JSON.stringify(LINKS_PLACEHOLDER), JSON.stringify([...urls]))
|
||||
.replace(JSON.stringify(STYLES_PLACEHOLDER), JSON.stringify([...stylesMap.values()])),
|
||||
.replace(JSON.stringify(STYLES_PLACEHOLDER), JSON.stringify([...stylesMap.values()]))
|
||||
.replace(JSON.stringify(SCRIPTS_PLACEHOLDER), JSON.stringify([...hoistedScripts])),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function astroBundleDelayedAssetPlugin({
|
||||
internals,
|
||||
}: {
|
||||
internals: BuildInternals;
|
||||
}): Plugin {
|
||||
export function astroContentProdBundlePlugin({ internals }: { internals: BuildInternals }): Plugin {
|
||||
return {
|
||||
name: 'astro-bundle-delayed-asset-plugin',
|
||||
name: 'astro:content-prod-bundle',
|
||||
async generateBundle(_options, bundle) {
|
||||
for (const [_, chunk] of Object.entries(bundle)) {
|
||||
if (chunk.type === 'chunk' && chunk.code.includes(LINKS_PLACEHOLDER)) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import fs from 'fs';
|
|||
import { bgGreen, bgMagenta, black, dim } from 'kleur/colors';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as vite from 'vite';
|
||||
import { astroBundleDelayedAssetPlugin } from '../../content/index.js';
|
||||
import { astroContentProdBundlePlugin } from '../../content/index.js';
|
||||
import {
|
||||
BuildInternals,
|
||||
createBuildInternals,
|
||||
|
@ -165,7 +165,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
|
|||
}),
|
||||
vitePluginPrerender(opts, internals),
|
||||
...(viteConfig.plugins || []),
|
||||
astroBundleDelayedAssetPlugin({ internals }),
|
||||
astroContentProdBundlePlugin({ internals }),
|
||||
// SSR needs to be last
|
||||
ssr && vitePluginSSR(internals, settings.adapter!),
|
||||
],
|
||||
|
|
|
@ -6,7 +6,7 @@ import { isCSSRequest } from '../render/util.js';
|
|||
import type { BuildInternals } from './internal';
|
||||
import type { PageBuildData, StaticBuildOptions } from './types';
|
||||
|
||||
import { DELAYED_ASSET_FLAG } from '../../content/consts.js';
|
||||
import { PROPAGATED_ASSET_FLAG } from '../../content/consts.js';
|
||||
import * as assetName from './css-asset-name.js';
|
||||
import { moduleIsTopLevelPage, walkParentInfos } from './graph.js';
|
||||
import {
|
||||
|
@ -79,7 +79,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
for (const [pageInfo] of walkParentInfos(id, {
|
||||
getModuleInfo: args[0].getModuleInfo,
|
||||
})) {
|
||||
if (new URL(pageInfo.id, 'file://').searchParams.has(DELAYED_ASSET_FLAG)) {
|
||||
if (new URL(pageInfo.id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) {
|
||||
// Split delayed assets to separate modules
|
||||
// so they can be injected where needed
|
||||
return createNameHash(id, [id]);
|
||||
|
@ -172,10 +172,10 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
id,
|
||||
this,
|
||||
function until(importer) {
|
||||
return new URL(importer, 'file://').searchParams.has(DELAYED_ASSET_FLAG);
|
||||
return new URL(importer, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG);
|
||||
}
|
||||
)) {
|
||||
if (new URL(pageInfo.id, 'file://').searchParams.has(DELAYED_ASSET_FLAG)) {
|
||||
if (new URL(pageInfo.id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) {
|
||||
for (const parent of walkParentInfos(id, this)) {
|
||||
const parentInfo = parent[0];
|
||||
if (moduleIsTopLevelPage(parentInfo)) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { crawlFrameworkPkgs } from 'vitefu';
|
|||
import {
|
||||
astroContentServerPlugin,
|
||||
astroContentVirtualModPlugin,
|
||||
astroDelayedAssetPlugin,
|
||||
astroContentAssetPropagationPlugin,
|
||||
} from '../content/index.js';
|
||||
import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
|
||||
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
|
||||
|
@ -106,7 +106,7 @@ export async function createVite(
|
|||
astroInjectEnvTsPlugin({ settings, logging, fs }),
|
||||
astroContentVirtualModPlugin({ settings }),
|
||||
astroContentServerPlugin({ fs, settings, logging, mode }),
|
||||
astroDelayedAssetPlugin({ mode }),
|
||||
astroContentAssetPropagationPlugin({ mode }),
|
||||
],
|
||||
publicDir: fileURLToPath(settings.config.publicDir),
|
||||
root: fileURLToPath(settings.config.root),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { ModuleLoader, ModuleNode } from '../../module-loader/index';
|
||||
|
||||
import npath from 'path';
|
||||
import { DELAYED_ASSET_FLAG } from '../../../content/consts.js';
|
||||
import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js';
|
||||
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../../constants.js';
|
||||
import { unwrapId } from '../../util.js';
|
||||
import { STYLE_EXTENSIONS } from '../util.js';
|
||||
|
@ -23,7 +23,7 @@ export async function* crawlGraph(
|
|||
): AsyncGenerator<ModuleNode, void, unknown> {
|
||||
const id = unwrapId(_id);
|
||||
const importedModules = new Set<ModuleNode>();
|
||||
if (new URL(id, 'file://').searchParams.has(DELAYED_ASSET_FLAG)) return;
|
||||
if (new URL(id, 'file://').searchParams.has(PROPAGATED_ASSET_FLAG)) return;
|
||||
|
||||
const moduleEntriesForId = isRootFile
|
||||
? // "getModulesByFile" pulls from a delayed module cache (fun implementation detail),
|
||||
|
|
|
@ -18,6 +18,7 @@ export {
|
|||
renderPage,
|
||||
renderSlot,
|
||||
renderStyleElement,
|
||||
renderScriptElement,
|
||||
renderTemplate as render,
|
||||
renderTemplate,
|
||||
renderToString,
|
||||
|
|
|
@ -16,6 +16,6 @@ export { renderHTMLElement } from './dom.js';
|
|||
export { maybeRenderHead, renderHead } from './head.js';
|
||||
export { renderPage } from './page.js';
|
||||
export { renderSlot } from './slot.js';
|
||||
export { renderStyleElement, renderUniqueStylesheet } from './tags.js';
|
||||
export { renderStyleElement, renderScriptElement, renderUniqueStylesheet } from './tags.js';
|
||||
export type { RenderInstruction } from './types';
|
||||
export { addAttribute, defineScriptVars, voidElementNames } from './util.js';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SSRResult } from '../../../@types/astro';
|
||||
import { SSRElement, SSRResult } from '../../../@types/astro';
|
||||
import { renderElement } from './util.js';
|
||||
|
||||
const stylesheetRel = 'stylesheet';
|
||||
|
@ -10,6 +10,13 @@ export function renderStyleElement(children: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function renderScriptElement({ props, children }: SSRElement) {
|
||||
return renderElement('script', {
|
||||
props,
|
||||
children,
|
||||
});
|
||||
}
|
||||
|
||||
export function renderStylesheet({ href }: { href: string }) {
|
||||
return renderElement(
|
||||
'link',
|
||||
|
|
166
packages/astro/test/content-collections-render.test.js
Normal file
166
packages/astro/test/content-collections-render.test.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { loadFixture, isWindows } from './test-utils.js';
|
||||
import testAdapter from './test-adapter.js';
|
||||
|
||||
const describe = isWindows ? global.describe.skip : global.describe;
|
||||
|
||||
describe('Content Collections - render()', () => {
|
||||
describe('Build - SSG', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/content/',
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('Includes CSS for rendered entry', async () => {
|
||||
const html = await fixture.readFile('/launch-week/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Renders content
|
||||
expect($('ul li')).to.have.a.lengthOf(3);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
it('Excludes CSS for non-rendered entries', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Excludes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
||||
});
|
||||
|
||||
it('Includes component scripts for rendered entry', async () => {
|
||||
const html = await fixture.readFile('/launch-week-component-scripts/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const allScripts = $('head > script[type="module"]');
|
||||
expect(allScripts).to.have.length;
|
||||
|
||||
// Includes hoisted script
|
||||
expect(
|
||||
[...allScripts].find((script) =>
|
||||
$(script).text().includes('document.querySelector("#update-me")')
|
||||
),
|
||||
'`WithScripts.astro` hoisted script missing from head.'
|
||||
).to.not.be.undefined;
|
||||
|
||||
// Includes inline script
|
||||
expect($('script[data-is-inline]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
// TODO: Script bleed isn't solved for prod builds.
|
||||
// Tackling in separate PR.
|
||||
it.skip('Excludes component scripts for non-rendered entries', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const allScripts = $('head > script[type="module"]');
|
||||
|
||||
// Excludes hoisted script
|
||||
expect(
|
||||
[...allScripts].find((script) =>
|
||||
$(script).text().includes('document.querySelector("#update-me")')
|
||||
),
|
||||
'`WithScripts.astro` hoisted script included unexpectedly.'
|
||||
).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Build - SSR', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
output: 'server',
|
||||
root: './fixtures/content/',
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('Includes CSS for rendered entry', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/launch-week');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Renders content
|
||||
expect($('ul li')).to.have.a.lengthOf(3);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
it('Exclude CSS for non-rendered entries', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dev - SSG', () => {
|
||||
let devServer;
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/content/',
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await devServer.stop();
|
||||
});
|
||||
|
||||
it('Includes CSS for rendered entry', async () => {
|
||||
const response = await fixture.fetch('/launch-week', { method: 'GET' });
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Renders content
|
||||
expect($('ul li')).to.have.a.lengthOf(3);
|
||||
|
||||
// Includes styles
|
||||
expect($('head > style')).to.have.a.lengthOf(1);
|
||||
expect($('head > style').text()).to.include("font-family: 'Comic Sans MS'");
|
||||
});
|
||||
|
||||
it('Includes component scripts for rendered entry', async () => {
|
||||
const response = await fixture.fetch('/launch-week-component-scripts', { method: 'GET' });
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const allScripts = $('head > script[src]');
|
||||
expect(allScripts).to.have.length;
|
||||
|
||||
// Includes hoisted script
|
||||
expect(
|
||||
[...allScripts].find((script) => script.attribs.src.includes('WithScripts.astro')),
|
||||
'`WithScripts.astro` hoisted script missing from head.'
|
||||
).to.not.be.undefined;
|
||||
|
||||
// Includes inline script
|
||||
expect($('script[data-is-inline]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
});
|
||||
});
|
11
packages/astro/test/fixtures/content/src/components/WithScripts.astro
vendored
Normal file
11
packages/astro/test/fixtures/content/src/components/WithScripts.astro
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<p id="update-me">Hoisted script didn't update me :(</p>
|
||||
|
||||
<p id="update-me-inline">Inline script didn't update me :(</p>
|
||||
|
||||
<script>
|
||||
document.querySelector('#update-me').innerText = 'Updated client-side with hoisted script!';
|
||||
</script>
|
||||
|
||||
<script is:inline data-is-inline>
|
||||
document.querySelector('#update-me-inline').innerText = 'Updated client-side with inline script!';
|
||||
</script>
|
16
packages/astro/test/fixtures/content/src/content/blog/promo/launch-week-component-scripts.mdx
vendored
Normal file
16
packages/astro/test/fixtures/content/src/content/blog/promo/launch-week-component-scripts.mdx
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: 'Launch week!'
|
||||
description: 'Join us for the exciting launch of SPACE BLOG'
|
||||
publishedDate: 'Sat May 21 2022 00:00:00 GMT-0400 (Eastern Daylight Time)'
|
||||
tags: ['announcement']
|
||||
---
|
||||
|
||||
import WithScripts from '../../../components/WithScripts.astro';
|
||||
|
||||
Join us for the space blog launch!
|
||||
|
||||
- THIS THURSDAY
|
||||
- Houston, TX
|
||||
- Dress code: **interstellar casual** ✨
|
||||
|
||||
<WithScripts />
|
|
@ -5,7 +5,7 @@ publishedDate: 'Sat May 21 2022 00:00:00 GMT-0400 (Eastern Daylight Time)'
|
|||
tags: ['announcement']
|
||||
---
|
||||
|
||||
import './launch-week-styles.css';
|
||||
import './_launch-week-styles.css';
|
||||
|
||||
Join us for the space blog launch!
|
||||
|
||||
|
|
14
packages/astro/test/fixtures/content/src/pages/launch-week-component-scripts.astro
vendored
Normal file
14
packages/astro/test/fixtures/content/src/pages/launch-week-component-scripts.astro
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
import { getEntryBySlug } from 'astro:content';
|
||||
|
||||
const entry = await getEntryBySlug('blog', 'promo/launch-week-component-scripts');
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
<title>Launch Week</title>
|
||||
</head>
|
||||
<body>
|
||||
<Content />
|
||||
</body>
|
||||
</html>
|
|
@ -1,9 +1,8 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import { getEntryBySlug } from 'astro:content';
|
||||
|
||||
const blog = await getCollection('blog');
|
||||
const launchWeekEntry = blog.find(post => post.id === 'promo/launch-week.mdx');
|
||||
const { Content } = await launchWeekEntry.render();
|
||||
const entry = await getEntryBySlug('blog', 'promo/launch-week');
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
<html>
|
||||
<head>
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
import { loadFixture, isWindows } from './test-utils.js';
|
||||
import testAdapter from './test-adapter.js';
|
||||
|
||||
const describe = isWindows ? global.describe.skip : global.describe;
|
||||
|
||||
describe('Content Collections - render()', () => {
|
||||
describe('Build - SSG', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/content/',
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('Page that render an entry include its CSS', async () => {
|
||||
const html = await fixture.readFile('/launch-week/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Renders content
|
||||
expect($('ul li')).to.have.a.lengthOf(3);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
it('Page that does not render an entry does not include its CSS', async () => {
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Build - SSR', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({
|
||||
output: 'server',
|
||||
root: './fixtures/content/',
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
await fixture.build();
|
||||
});
|
||||
|
||||
it('Page that render an entry include its CSS', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/launch-week');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Renders content
|
||||
expect($('ul li')).to.have.a.lengthOf(3);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);
|
||||
});
|
||||
|
||||
it('Page that does not render an entry does not include its CSS', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/');
|
||||
const response = await app.render(request);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// Includes styles
|
||||
expect($('link[rel=stylesheet]')).to.have.a.lengthOf(0);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue