Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
|
53cd065c96 | ||
|
69b6dbed54 | ||
|
b224b31140 | ||
|
6b10fbce54 | ||
|
b81f816912 | ||
|
a6f42fa4d7 |
11 changed files with 92 additions and 4 deletions
18
.changeset/brave-pots-drop.md
Normal file
18
.changeset/brave-pots-drop.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
'astro': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Page Fragments
|
||||||
|
|
||||||
|
Any page components can now be identified as *fragments*, allowing you fetch them in the client in order to replace only parts of the page. Fragments are used in conjuction with a partial rendering library, like htmx or Stimulus or even just jQuery.
|
||||||
|
|
||||||
|
Pages marked as fragments do not have a `doctype` or any head content included in the rendered result. You can mark any page as a fragment by setting this option:
|
||||||
|
|
||||||
|
|
||||||
|
```astro
|
||||||
|
---
|
||||||
|
export const fragment = true;
|
||||||
|
---
|
||||||
|
|
||||||
|
<li>This is a single list item.</li>
|
||||||
|
```
|
|
@ -1520,6 +1520,7 @@ export type AsyncRendererComponentFn<U> = (
|
||||||
export interface ComponentInstance {
|
export interface ComponentInstance {
|
||||||
default: AstroComponentFactory;
|
default: AstroComponentFactory;
|
||||||
css?: string[];
|
css?: string[];
|
||||||
|
fragment?: boolean;
|
||||||
prerender?: boolean;
|
prerender?: boolean;
|
||||||
/**
|
/**
|
||||||
* Only used for logging if deprecated drafts feature is used
|
* Only used for logging if deprecated drafts feature is used
|
||||||
|
@ -2191,6 +2192,7 @@ export interface SSRResult {
|
||||||
*/
|
*/
|
||||||
clientDirectives: Map<string, string>;
|
clientDirectives: Map<string, string>;
|
||||||
compressHTML: boolean;
|
compressHTML: boolean;
|
||||||
|
fragment: boolean;
|
||||||
/**
|
/**
|
||||||
* Only used for logging
|
* Only used for logging
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -49,6 +49,7 @@ export async function renderPage({ mod, renderContext, env, cookies }: RenderPag
|
||||||
clientDirectives: env.clientDirectives,
|
clientDirectives: env.clientDirectives,
|
||||||
compressHTML: env.compressHTML,
|
compressHTML: env.compressHTML,
|
||||||
request: renderContext.request,
|
request: renderContext.request,
|
||||||
|
fragment: !!mod.fragment,
|
||||||
site: env.site,
|
site: env.site,
|
||||||
scripts: renderContext.scripts,
|
scripts: renderContext.scripts,
|
||||||
ssr: env.ssr,
|
ssr: env.ssr,
|
||||||
|
|
|
@ -31,6 +31,7 @@ export interface CreateResultArgs {
|
||||||
renderers: SSRLoadedRenderer[];
|
renderers: SSRLoadedRenderer[];
|
||||||
clientDirectives: Map<string, string>;
|
clientDirectives: Map<string, string>;
|
||||||
compressHTML: boolean;
|
compressHTML: boolean;
|
||||||
|
fragment: boolean;
|
||||||
resolve: (s: string) => Promise<string>;
|
resolve: (s: string) => Promise<string>;
|
||||||
/**
|
/**
|
||||||
* Used for `Astro.site`
|
* Used for `Astro.site`
|
||||||
|
@ -155,6 +156,7 @@ export function createResult(args: CreateResultArgs): SSRResult {
|
||||||
renderers: args.renderers,
|
renderers: args.renderers,
|
||||||
clientDirectives: args.clientDirectives,
|
clientDirectives: args.clientDirectives,
|
||||||
compressHTML: args.compressHTML,
|
compressHTML: args.compressHTML,
|
||||||
|
fragment: args.fragment,
|
||||||
pathname: args.pathname,
|
pathname: args.pathname,
|
||||||
cookies,
|
cookies,
|
||||||
/** This function returns the `Astro` faux-global */
|
/** This function returns the `Astro` faux-global */
|
||||||
|
|
|
@ -33,7 +33,7 @@ export async function renderToString(
|
||||||
// Automatic doctype insertion for pages
|
// Automatic doctype insertion for pages
|
||||||
if (isPage && !renderedFirstPageChunk) {
|
if (isPage && !renderedFirstPageChunk) {
|
||||||
renderedFirstPageChunk = true;
|
renderedFirstPageChunk = true;
|
||||||
if (!/<!doctype html/i.test(String(chunk))) {
|
if (!result.fragment && !/<!doctype html/i.test(String(chunk))) {
|
||||||
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
||||||
str += doctype;
|
str += doctype;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ export async function renderToReadableStream(
|
||||||
// Automatic doctype insertion for pages
|
// Automatic doctype insertion for pages
|
||||||
if (isPage && !renderedFirstPageChunk) {
|
if (isPage && !renderedFirstPageChunk) {
|
||||||
renderedFirstPageChunk = true;
|
renderedFirstPageChunk = true;
|
||||||
if (!/<!doctype html/i.test(String(chunk))) {
|
if (!result.fragment && !/<!doctype html/i.test(String(chunk))) {
|
||||||
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
||||||
controller.enqueue(encoder.encode(doctype));
|
controller.enqueue(encoder.encode(doctype));
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,13 +77,13 @@ function stringifyChunk(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'head': {
|
case 'head': {
|
||||||
if (result._metadata.hasRenderedHead) {
|
if (result._metadata.hasRenderedHead || result.fragment) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return renderAllHeadContent(result);
|
return renderAllHeadContent(result);
|
||||||
}
|
}
|
||||||
case 'maybe-head': {
|
case 'maybe-head': {
|
||||||
if (result._metadata.hasRenderedHead || result._metadata.headInTree) {
|
if (result._metadata.hasRenderedHead || result._metadata.headInTree || result.fragment) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return renderAllHeadContent(result);
|
return renderAllHeadContent(result);
|
||||||
|
|
5
packages/astro/test/fixtures/fragments/astro.config.mjs
vendored
Normal file
5
packages/astro/test/fixtures/fragments/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
});
|
8
packages/astro/test/fixtures/fragments/package.json
vendored
Normal file
8
packages/astro/test/fixtures/fragments/package.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "@test/fragments",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
4
packages/astro/test/fixtures/fragments/src/pages/fragments/item.astro
vendored
Normal file
4
packages/astro/test/fixtures/fragments/src/pages/fragments/item.astro
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
export const fragment = true;
|
||||||
|
---
|
||||||
|
<li>This is a single line item</li>
|
42
packages/astro/test/fragments.test.js
Normal file
42
packages/astro/test/fragments.test.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { loadFixture } from './test-utils.js';
|
||||||
|
|
||||||
|
describe('Fragments', () => {
|
||||||
|
/** @type {import('./test-utils.js').Fixture} */
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: './fixtures/fragments/',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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('/fragments/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('/fragments/item/index.html');
|
||||||
|
expect(html.startsWith('<li>')).to.equal(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -2689,6 +2689,12 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../..
|
version: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/fragments:
|
||||||
|
dependencies:
|
||||||
|
astro:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/get-static-paths-pages:
|
packages/astro/test/fixtures/get-static-paths-pages:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro:
|
astro:
|
||||||
|
|
Loading…
Reference in a new issue