Skip to content

Commit

Permalink
Partials (#8755)
Browse files Browse the repository at this point in the history
* Fragment support

* Add a changeset

* Linting

* debuggin

* Rename to partial

* Update the chagneset

* Make work with mdx

* Update .changeset/brave-pots-drop.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

* Update .changeset/brave-pots-drop.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

* Update .changeset/brave-pots-drop.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

---------

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
  • Loading branch information
2 people authored and natemoo-re committed Nov 22, 2023
1 parent a6ab69c commit 260cde2
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 5 deletions.
24 changes: 24 additions & 0 deletions .changeset/brave-pots-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'astro': minor
---

Page Partials

A page component can now be identified as a **partial** page, which will render its HTML content without including a `<! DOCTYPE html>` declaration nor any `<head>` content.

A rendering library, like htmx or Stimulus or even just jQuery can access partial content on the client to dynamically update only parts of a page.

Pages marked as partials do not have a `doctype` or any head content included in the rendered result. You can mark any page as a partial by setting this option:


```astro
---
export const partial = true;
---
<li>This is a single list item.</li>
```

Other valid page files that can export a value (e.g. `.mdx`) can also be marked as partials.

Read more about [Astro page partials](/en/core-concepts/astro-pages/#partials) in our documentation.
2 changes: 2 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,7 @@ export type AsyncRendererComponentFn<U> = (
export interface ComponentInstance {
default: AstroComponentFactory;
css?: string[];
partial?: boolean;
prerender?: boolean;
/**
* Only used for logging if deprecated drafts feature is used
Expand Down Expand Up @@ -2234,6 +2235,7 @@ export interface SSRResult {
*/
clientDirectives: Map<string, string>;
compressHTML: boolean;
partial: boolean;
/**
* Only used for logging
*/
Expand Down
1 change: 1 addition & 0 deletions packages/astro/src/core/render/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag
clientDirectives: env.clientDirectives,
compressHTML: env.compressHTML,
request: renderContext.request,
partial: !!mod.partial,
site: env.site,
scripts: renderContext.scripts,
ssr: env.ssr,
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/core/render/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface CreateResultArgs {
renderers: SSRLoadedRenderer[];
clientDirectives: Map<string, string>;
compressHTML: boolean;
partial: boolean;
resolve: (s: string) => Promise<string>;
/**
* Used for `Astro.site`
Expand Down Expand Up @@ -155,6 +156,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
renderers: args.renderers,
clientDirectives: args.clientDirectives,
compressHTML: args.compressHTML,
partial: args.partial,
pathname: args.pathname,
cookies,
/** This function returns the `Astro` faux-global */
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/runtime/server/render/astro/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function renderToString(
// Automatic doctype insertion for pages
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!/<!doctype html/i.test(String(chunk))) {
if (!result.partial && !/<!doctype html/i.test(String(chunk))) {
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
str += doctype;
}
Expand Down Expand Up @@ -84,7 +84,7 @@ export async function renderToReadableStream(
// Automatic doctype insertion for pages
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!/<!doctype html/i.test(String(chunk))) {
if (!result.partial && !/<!doctype html/i.test(String(chunk))) {
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
controller.enqueue(encoder.encode(doctype));
}
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/runtime/server/render/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ function stringifyChunk(
}
}
case 'head': {
if (result._metadata.hasRenderedHead) {
if (result._metadata.hasRenderedHead || result.partial) {
return '';
}
return renderAllHeadContent(result);
}
case 'maybe-head': {
if (result._metadata.hasRenderedHead || result._metadata.headInTree) {
if (result._metadata.hasRenderedHead || result._metadata.headInTree || result.partial) {
return '';
}
return renderAllHeadContent(result);
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export async function renderComponentToString(
// Automatic doctype and head insertion for pages
if (isPage && !renderedFirstPageChunk) {
renderedFirstPageChunk = true;
if (!/<!doctype html/i.test(String(chunk))) {
if (!result.partial && !/<!doctype html/i.test(String(chunk))) {
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
str += doctype + head;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/astro/test/fixtures/partials/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [
mdx()
]
});
9 changes: 9 additions & 0 deletions packages/astro/test/fixtures/partials/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@test/partials",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const partial = true;

# This is heading

and this is some text
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
export const partial = true;
---
<li>This is a single line item</li>
47 changes: 47 additions & 0 deletions packages/astro/test/partials.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect } from 'chai';
import { loadFixture } from './test-utils.js';

describe('Partials', () => {
/** @type {import('./test-utils.js').Fixture} */
let fixture;

before(async () => {
fixture = await loadFixture({
root: './fixtures/partials/',
});
});

describe('dev', () => {
/** @type {import('./test-utils.js').DevServer} */
let devServer;

before(async () => {
devServer = await fixture.startDevServer();
});

after(async () => {
await devServer.stop();
});

it('is only the written HTML', async () => {
const html = await fixture.fetch('/partials/item/').then((res) => res.text());
expect(html.startsWith('<li>')).to.equal(true);
});
});

describe('build', () => {
before(async () => {
await fixture.build();
});

it('is only the written HTML', async () => {
const html = await fixture.readFile('/partials/item/index.html');
expect(html.startsWith('<li>')).to.equal(true);
});

it('Works with mdx', async () => {
const html = await fixture.readFile('/partials/docs/index.html');
expect(html.startsWith('<h1')).to.equal(true);
});
});
});

0 comments on commit 260cde2

Please sign in to comment.