Skip to content

Commit b6cc883

Browse files
bug/issue 1475 handle top level pages and layouts HTML output merging (#1498)
1 parent 8db460c commit b6cc883

71 files changed

Lines changed: 496 additions & 288 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/cli/src/lib/layout-utils.js

Lines changed: 243 additions & 195 deletions
Large diffs are not rendered by default.

packages/cli/src/lifecycles/bundle.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getRollupConfigForBrowserScripts,
66
getRollupConfigForSsrPages,
77
} from "../config/rollup.config.js";
8-
import { getAppLayout, getPageLayout, getUserScripts } from "../lib/layout-utils.js";
8+
import { getAppLayout, getPageLayout, getGreenwoodScripts } from "../lib/layout-utils.js";
99
import { hashString } from "../lib/hashing-utils.js";
1010
import {
1111
checkResourceExists,
@@ -328,7 +328,7 @@ async function bundleSsrPages(compilation, optimizePlugins) {
328328
// and before we optimize so that all bundled assets can tracked up front
329329
// would be nice to see if this can be done in a single pass though...
330330
for (const page of ssrPages) {
331-
const { imports, route, layout, pageHref } = page;
331+
const { route, pageHref } = page;
332332
const moduleUrl = new URL(pageHref);
333333
const request = new Request(moduleUrl);
334334
const data = await executeRouteModule({
@@ -340,11 +340,12 @@ async function bundleSsrPages(compilation, optimizePlugins) {
340340
scripts: [],
341341
request,
342342
});
343-
let staticHtml = "";
343+
let staticHtml = "<content-outlet></content-outlet>";
344+
345+
staticHtml = await getPageLayout(staticHtml, compilation, page, data.layout);
346+
staticHtml = await getAppLayout(staticHtml, compilation, page);
347+
staticHtml = await getGreenwoodScripts(staticHtml, compilation);
344348

345-
staticHtml = data.layout ? data.layout : await getPageLayout(pageHref, compilation, layout);
346-
staticHtml = await getAppLayout(staticHtml, compilation, imports, page);
347-
staticHtml = await getUserScripts(staticHtml, compilation);
348349
staticHtml = await (
349350
await interceptPage(
350351
new URL(`http://localhost:8080${route}`),

packages/cli/src/lifecycles/graph.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ const generateGraph = async (compilation) => {
143143
});
144144
} else if (isPage) {
145145
let root = filename.split("/")[filename.split("/").length - 1].replace(extension, "");
146-
let layout = extension === ".html" ? null : "page";
146+
// should we even have a default page layout?
147+
// https://github.com/ProjectEvergreen/greenwood/issues/1271
148+
let layout = "page";
147149
let title = null;
148150
let label = getLabelFromRoute(`${route}/`);
149151
let imports = [];

packages/cli/src/plugins/resource/plugin-standard-html.js

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import rehypeRaw from "rehype-raw";
1111
import remarkFrontmatter from "remark-frontmatter";
1212
import remarkParse from "remark-parse";
1313
import remarkRehype from "remark-rehype";
14-
import { getUserScripts, getPageLayout, getAppLayout } from "../../lib/layout-utils.js";
15-
import { requestAsObject } from "../../lib/resource-utils.js";
14+
import { getPageLayout, getAppLayout, getGreenwoodScripts } from "../../lib/layout-utils.js";
15+
import { requestAsObject, checkResourceExists } from "../../lib/resource-utils.js";
1616
import { unified } from "unified";
1717
import { Worker } from "worker_threads";
1818
import { parse as htmlparser } from "node-html-parser";
@@ -37,26 +37,32 @@ class StandardHtmlResource {
3737

3838
async serve(url, request) {
3939
const { config, context } = this.compilation;
40-
const { userWorkspace } = context;
40+
const { userWorkspace, pagesDir, layoutsDir } = context;
4141
const { pathname } = url;
4242
const isSpaRoute = this.compilation.graph.find((node) => node.isSPA);
4343
const matchingRoute = this.compilation.graph.find((node) => node.route === pathname) || {};
44-
const { pageHref } = matchingRoute;
44+
const { pageHref, route } = matchingRoute;
4545
const filePath =
4646
!matchingRoute.external && pageHref
4747
? new URL(pageHref).pathname.replace(userWorkspace.pathname, "./")
4848
: "";
4949
const isMarkdownContent = (filePath || "").split(".").pop() === "md";
50+
const isHtmlContent = (filePath || "").split(".").pop() === "html";
5051
let body = "";
51-
let layout = matchingRoute.layout || null;
52-
let customImports = matchingRoute.imports || [];
5352
let ssrBody;
5453
let ssrLayout;
5554
let processedMarkdown = null;
56-
57-
if (matchingRoute.external) {
58-
layout = matchingRoute.layout || layout;
59-
}
55+
// final contents to return from the plugin
56+
let html = "";
57+
58+
const customPageFormatPlugins = config.plugins
59+
.filter((plugin) => plugin.type === "resource" && !plugin.isGreenwoodDefaultPlugin)
60+
.map((plugin) => plugin.provider(this.compilation));
61+
const isCustomStaticPage =
62+
customPageFormatPlugins[0] &&
63+
customPageFormatPlugins[0].servePage === "static" &&
64+
customPageFormatPlugins[0].shouldServe &&
65+
(await customPageFormatPlugins[0].shouldServe(new URL(pageHref)));
6066

6167
if (isMarkdownContent) {
6268
const markdownContents = await fs.readFile(new URL(pageHref), "utf-8");
@@ -85,9 +91,21 @@ class StandardHtmlResource {
8591
.use(rehypePlugins) // apply userland rehype plugins
8692
.use(rehypeStringify) // convert AST to HTML string
8793
.process(markdownContents);
88-
}
94+
} else if (isHtmlContent) {
95+
if (route.endsWith("/404/")) {
96+
const pathUrl = (await checkResourceExists(new URL("./404.html", pagesDir)))
97+
? new URL("./404.html", pagesDir)
98+
: new URL("./404.html", layoutsDir);
99+
100+
body = await fs.readFile(pathUrl, "utf-8");
101+
} else {
102+
body = await fs.readFile(new URL(pageHref), "utf-8");
103+
}
104+
} else if (isCustomStaticPage) {
105+
const transformed = await customPageFormatPlugins[0].serve(new URL(pageHref));
89106

90-
if (matchingRoute.isSSR) {
107+
body = await transformed.text();
108+
} else if (matchingRoute.isSSR) {
91109
const routeModuleLocationUrl = new URL(pageHref);
92110
const routeWorkerUrl = this.compilation.config.plugins
93111
.find((plugin) => plugin.type === "renderer")
@@ -124,15 +142,6 @@ class StandardHtmlResource {
124142
});
125143
}
126144

127-
if (isSpaRoute) {
128-
body = await fs.readFile(new URL(isSpaRoute.pageHref), "utf-8");
129-
} else {
130-
body = ssrLayout ? ssrLayout : await getPageLayout(pageHref, this.compilation, layout);
131-
}
132-
133-
body = await getAppLayout(body, this.compilation, customImports, matchingRoute);
134-
body = await getUserScripts(body, this.compilation);
135-
136145
if (processedMarkdown) {
137146
const wrappedCustomElementRegex =
138147
/<p><[a-zA-Z]*-[a-zA-Z](.*)>(.*)<\/[a-zA-Z]*-[a-zA-Z](.*)><\/p>/g;
@@ -149,25 +158,43 @@ class StandardHtmlResource {
149158
}
150159

151160
// https://github.com/ProjectEvergreen/greenwood/issues/1126
152-
body = body.replace(
153-
/<content-outlet>(.*)<\/content-outlet>/s,
154-
processedMarkdown.value.replace(/\$/g, "$$$"),
155-
);
161+
body = processedMarkdown.value.replace(/\$/g, "$$$");
156162
} else if (matchingRoute.external) {
157-
body = body.replace(/<content-outlet>(.*)<\/content-outlet>/s, matchingRoute.body);
163+
body = matchingRoute.body;
158164
} else if (ssrBody) {
159-
body = body.replace(
160-
/<content-outlet>(.*)<\/content-outlet>/s,
161-
`<!-- greenwood-ssr-start -->${ssrBody.replace(/\$/g, "$$$")}<!-- greenwood-ssr-end -->`,
165+
// we wrap SSR content in comments so we can extract it during prerendering to avoid double pre-rendering
166+
body = `<!-- greenwood-ssr-start -->${ssrBody.replace(/\$/g, "$$$")}<!-- greenwood-ssr-end -->`;
167+
}
168+
169+
if (isSpaRoute) {
170+
html = await fs.readFile(new URL(isSpaRoute.pageHref), "utf-8");
171+
} else {
172+
const mergedPageLayoutContents = await getPageLayout(
173+
body,
174+
this.compilation,
175+
matchingRoute,
176+
ssrLayout,
177+
);
178+
179+
const mergedAppLayoutContents = await getAppLayout(
180+
mergedPageLayoutContents,
181+
this.compilation,
182+
matchingRoute,
162183
);
184+
185+
html = mergedAppLayoutContents;
163186
}
164187

188+
html = await getGreenwoodScripts(html, this.compilation);
189+
165190
// clean up any empty placeholder content-outlet
166-
if (body.indexOf("<content-outlet></content-outlet>") > 0) {
167-
body = body.replace("<content-outlet></content-outlet>", "");
191+
// TODO do we even want this?
192+
// https://github.com/ProjectEvergreen/greenwood/issues/1271
193+
if (html.indexOf("<content-outlet></content-outlet>") > 0) {
194+
html = html.replace("<content-outlet></content-outlet>", "");
168195
}
169196

170-
return new Response(body, {
197+
return new Response(html, {
171198
headers: new Headers({
172199
"Content-Type": this.contentType,
173200
}),

packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ describe("Build Greenwood With: ", function () {
9292
);
9393
});
9494

95+
it("should have the correct value for the <title> tag in the <head> for the first post page", function () {
96+
const title = dom.window.document.querySelector("head title").textContent;
97+
98+
expect(title).to.be.equal("My First Post");
99+
});
100+
95101
it("should have the correct value for author <meta> tag in the <head>", function () {
96102
const authorMeta = dom.window.document
97103
.querySelector("head meta[name=author]")
@@ -122,6 +128,12 @@ describe("Build Greenwood With: ", function () {
122128
);
123129
});
124130

131+
it("should have the correct value for the <title> tag in the <head> for second post page", function () {
132+
const title = dom.window.document.querySelector("head title").textContent;
133+
134+
expect(title).to.be.equal("My Second Post");
135+
});
136+
125137
it("should have the correct songs frontmatter data in the page output", function () {
126138
const contents = dom.window.document.querySelector("body span").innerHTML;
127139
const songs = JSON.parse(contents);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: My First Post
3+
layout: blog
4+
published: 11/11/2022
5+
author: Owen Buckley
6+
---
7+
8+
<html>
9+
<body>
10+
<h1>My First Post</h1>
11+
<h3>Published: ${globalThis.page.data.published}</h3>
12+
<h4>Author: ${globalThis.page.data.author}</h4>
13+
<p>Lorum Ipsum.</p>
14+
</body>
15+
</html>

packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/first-post.md

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: My Second Post
3+
layout: blog
4+
published: 11/11/2022
5+
author: Owen Buckley
6+
songs:
7+
- title: Song 1
8+
url: song1.mp3
9+
- title: Song 2
10+
url: song2.mp3
11+
---
12+
13+
<html>
14+
<body>
15+
<h1>My Second Post</h1>
16+
<h2>Playlist</h2>
17+
<span>${globalThis.page.data.songs}</span>
18+
</body>
19+
</html>

packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/second-post.md

Lines changed: 0 additions & 17 deletions
This file was deleted.

packages/cli/test/cases/build.default.spa/build.default.spa.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe("Build Greenwood With: ", function () {
5252
runner.runCommand(cliPath, "build");
5353
});
5454

55-
runSmokeTest(["public", "index"], LABEL);
55+
runSmokeTest(["public"], LABEL);
5656

5757
describe("SPA (Single Page Application)", function () {
5858
let dom;

0 commit comments

Comments
 (0)