From df8fdff00eeacc4e2b98f2dd7dda2f38d8c07fc6 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 10 Aug 2024 16:06:51 -0400 Subject: [PATCH 01/36] initial implementation of content collections with rich frontmatter support --- packages/cli/src/data/queries.js | 66 +++++++++++++++++++ packages/cli/src/lifecycles/graph.js | 19 +++++- .../resource/plugin-content-as-data.js | 54 +++++++++++++++ .../plugins/resource/plugin-standard-html.js | 20 +++++- .../cli/src/plugins/server/plugin-content.js | 47 +++++++++++++ 5 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 packages/cli/src/data/queries.js create mode 100644 packages/cli/src/plugins/resource/plugin-content-as-data.js create mode 100644 packages/cli/src/plugins/server/plugin-content.js diff --git a/packages/cli/src/data/queries.js b/packages/cli/src/data/queries.js new file mode 100644 index 000000000..f86638d02 --- /dev/null +++ b/packages/cli/src/data/queries.js @@ -0,0 +1,66 @@ +// TODO how to sync host and port with greenwood config +const host = 'localhost'; +const port = 1985; + +async function getCollection(collection = '') { + return (await fetch(`http://${host}:${port}/graph.json`) + .then(resp => resp.json())) + .filter(page => page?.data?.collection === collection); +} + +async function getCollectionByRoute(route = '') { + return (await fetch(`http://${host}:${port}/graph.json`) + .then(resp => resp.json())) + .filter(page => page?.route.startsWith(route)); +} + +async function getContent() { + return await fetch(`http://${host}:${port}/graph.json`) + .then(resp => resp.json()); +} + +export { getContent, getCollection, getCollectionByRoute }; +// import { getQueryHash } from './common.js'; + +// const client = { +// query: (params) => { +// const { query, variables = {} } = params; + +// return fetch('http://localhost:4000/graphql', { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// 'Accept': 'application/json' +// }, +// body: JSON.stringify({ +// query, +// variables +// }) +// }).then((response) => response.json()); +// } +// }; + +// const APOLLO_STATE = globalThis.__APOLLO_STATE__; // eslint-disable-line no-underscore-dangle +// const BASE_PATH = globalThis.__GWD_BASE_PATH__; // eslint-disable-line no-underscore-dangle +// const backupQuery = client.query; + +// client.query = (params) => { +// if (APOLLO_STATE) { +// // __APOLLO_STATE__ defined, in production mode +// const queryHash = getQueryHash(params.query, params.variables); +// const cachePath = `${BASE_PATH}/${queryHash}-cache.json`; + +// return fetch(cachePath) +// .then(response => response.json()) +// .then((response) => { +// return { +// data: response +// }; +// }); +// } else { +// // __APOLLO_STATE__ NOT defined, in development mode +// return backupQuery(params); +// } +// }; + +// export default client; \ No newline at end of file diff --git a/packages/cli/src/lifecycles/graph.js b/packages/cli/src/lifecycles/graph.js index 08055f0ae..68c8a1989 100644 --- a/packages/cli/src/lifecycles/graph.js +++ b/packages/cli/src/lifecycles/graph.js @@ -12,6 +12,7 @@ const generateGraph = async (compilation) => { const { context, config } = compilation; const { basePath } = config; const { pagesDir, projectDirectory, userWorkspace } = context; + const collections = {}; const customPageFormatPlugins = config.plugins .filter(plugin => plugin.type === 'resource' && !plugin.isGreenwoodDefaultPlugin) .map(plugin => plugin.provider(compilation)); @@ -263,7 +264,7 @@ const generateGraph = async (compilation) => { * hydration: if this page needs hydration support * servePage: signal that this is a custom page file type (static | dynamic) */ - pages.push({ + const page = { data: customData || {}, filename, id, @@ -286,7 +287,21 @@ const generateGraph = async (compilation) => { isolation, hydration, servePage: isCustom - }); + }; + + pages.push(page); + + const pageCollection = customData.collection; + + if (pageCollection) { + if (!collections[pageCollection]) { + collections[pageCollection] = []; + } + + collections[pageCollection].push(page); + } + + compilation.collections = collections; } else { console.debug(`Unhandled extension (${extension}) for route => ${route}`); } diff --git a/packages/cli/src/plugins/resource/plugin-content-as-data.js b/packages/cli/src/plugins/resource/plugin-content-as-data.js new file mode 100644 index 000000000..5d299debc --- /dev/null +++ b/packages/cli/src/plugins/resource/plugin-content-as-data.js @@ -0,0 +1,54 @@ +import { mergeImportMap } from '../../lib/walker-package-ranger.js'; +import { ResourceInterface } from '../../lib/resource-interface.js'; + +const importMap = { + '@greenwood/cli/src/data/queries.js': '/node_modules/@greenwood/cli/src/data/queries.js' +}; + +class ContentAsDataResource extends ResourceInterface { + constructor(compilation, options = {}) { + super(compilation, options); + + this.contentType = ['text/html']; + } + + async shouldIntercept(url, request, response) { + return response.headers.get('Content-Type')?.indexOf(this.contentType[0]) >= 0; + } + + async intercept(url, request, response) { + const body = await response.text(); + const newBody = mergeImportMap(body, importMap); + + // TODO how come we need to forward headers, shouldn't mergeResponse do that for us? + return new Response(newBody, { + headers: response.headers + }); + } + + // TODO graphql based hydration? + // async shouldOptimize(url, response) { + // return response.headers.get('Content-Type').indexOf(this.contentType[1]) >= 0; + // } + + // async optimize(url, response) { + // let body = await response.text(); + + // body = body.replace('', ` + // + // + // `); + + // return new Response(body); + // } +} + +const greenwoodPluginContentAsData = { + type: 'resource', + name: 'plugin-content-as-data:resource', + provider: (compilation) => new ContentAsDataResource(compilation) +}; + +export { greenwoodPluginContentAsData }; \ No newline at end of file diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 06223cf3d..0f07495d9 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -74,7 +74,7 @@ class StandardHtmlResource extends ResourceInterface { } const settings = config.markdown.settings || {}; - const fm = frontmatter(markdownContents); + const fm = frontmatter(markdownContents); // TODO we already got this once in the graph phase... processedMarkdown = await unified() .use(remarkParse, settings) // parse markdown into AST @@ -172,14 +172,30 @@ class StandardHtmlResource extends ResourceInterface { } if (interpolateFrontmatter) { + console.log({ frontMatter, matchingRoute }); + // TODO consolidate this + for (const fm in matchingRoute.data) { + console.log('11', { fm }); + const interpolatedFrontmatter = '\\$\\{globalThis.page.' + fm + '\\}'; + const needle = typeof matchingRoute.data[fm] === 'string' ? matchingRoute.data[fm] : JSON.stringify(matchingRoute.data[fm]).replace(/"/g, '"'); + console.log('replace', needle); + body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), needle); + } + for (const fm in frontMatter) { const interpolatedFrontmatter = '\\$\\{globalThis.page.' + fm + '\\}'; body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), frontMatter[fm]); } + + for (const collection in this.compilation.collections) { + const interpolatedFrontmatter = '\\$\\{globalThis.collection.' + collection + '\\}'; + + body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), JSON.stringify(this.compilation.collections[collection]).replace(/"/g, '"')); + } } - // clean up placeholder content-outlet + // clean up any empty placeholder content-outlet if (body.indexOf('') > 0) { body = body.replace('', ''); } diff --git a/packages/cli/src/plugins/server/plugin-content.js b/packages/cli/src/plugins/server/plugin-content.js new file mode 100644 index 000000000..5023e6a3b --- /dev/null +++ b/packages/cli/src/plugins/server/plugin-content.js @@ -0,0 +1,47 @@ +import Koa from 'koa'; +import { ServerInterface } from '../../lib/server-interface.js'; +import { Readable } from 'stream'; + +class ContentServer extends ServerInterface { + constructor(compilation, options = {}) { + super(compilation, options); + } + + async start() { + const app = new Koa(); + + app.use(async (ctx, next) => { + try { + if (ctx.request.path.startsWith('/graph.json')) { + const { graph } = this.compilation; + + ctx.body = Readable.from(JSON.stringify(graph)); + ctx.status = 200; + ctx.message = 'OK'; + + ctx.set('Content-Type', 'application/json'); + ctx.set('Access-Control-Allow-Origin', '*'); + } + } catch (e) { + ctx.status = 500; + console.error(e); + } + + await next(); + }); + + // TODO use dev server +1 + await app.listen('1985', () => { + console.log('Started content server at => http://localhost:1985'); + }); + } +} + +// TODO remove graph.json resolution from regular dev server? +const greenwoodPluginContentServer = { + type: 'server', + name: 'plugin-content-server', + provider: (compilation) => new ContentServer(compilation) +}; + +export { greenwoodPluginContentServer }; \ No newline at end of file From 8db9df54ec2c59da0b7451c6ca5af633619606ee Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 10 Aug 2024 20:45:54 -0400 Subject: [PATCH 02/36] add test cases for collections and prerendering --- packages/cli/src/data/queries.js | 16 +- ...ild.config.interpolate-frontmatter.spec.js | 27 ++- .../src/pages/blog/second-post.md | 17 ++ ...build.config.prerender-collections.spec.js | 172 ++++++++++++++++++ .../greenwood.config.js | 3 + .../package.json | 3 + .../src/components/blog-posts-list.js | 26 +++ .../src/components/header.js | 29 +++ .../src/components/toc.js | 24 +++ .../src/pages/blog/first-post.md | 3 + .../src/pages/blog/index.html | 14 ++ .../src/pages/blog/second-post.md | 3 + .../src/pages/index.html | 14 ++ .../src/pages/toc.html | 14 ++ .../build.config.prerender.spec.js | 2 +- 15 files changed, 356 insertions(+), 11 deletions(-) create mode 100644 packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md create mode 100644 packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js create mode 100644 packages/cli/test/cases/build.config.prerender-collections/greenwood.config.js create mode 100644 packages/cli/test/cases/build.config.prerender-collections/package.json create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/components/blog-posts-list.js create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/components/header.js create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/components/toc.js create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/first-post.md create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/index.html create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/second-post.md create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/pages/index.html create mode 100644 packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html diff --git a/packages/cli/src/data/queries.js b/packages/cli/src/data/queries.js index f86638d02..f4b5f153a 100644 --- a/packages/cli/src/data/queries.js +++ b/packages/cli/src/data/queries.js @@ -2,24 +2,24 @@ const host = 'localhost'; const port = 1985; -async function getCollection(collection = '') { +async function getContent() { + return await fetch(`http://${host}:${port}/graph.json`) + .then(resp => resp.json()); +} + +async function getContentByCollection(collection = '') { return (await fetch(`http://${host}:${port}/graph.json`) .then(resp => resp.json())) .filter(page => page?.data?.collection === collection); } -async function getCollectionByRoute(route = '') { +async function getContentByRoute(route = '') { return (await fetch(`http://${host}:${port}/graph.json`) .then(resp => resp.json())) .filter(page => page?.route.startsWith(route)); } -async function getContent() { - return await fetch(`http://${host}:${port}/graph.json`) - .then(resp => resp.json()); -} - -export { getContent, getCollection, getCollectionByRoute }; +export { getContent, getContentByCollection, getContentByRoute }; // import { getQueryHash } from './common.js'; // const client = { diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js b/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js index b39193067..544fabe83 100644 --- a/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js +++ b/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js @@ -1,6 +1,6 @@ /* * Use Case - * Run Greenwood with interpolateFrontmatter configuration enabled. + * Run Greenwood with interpolateFrontmatter configuration enabled for simple and rich frontmatter. * * User Result * Should generate a bare bones Greenwood build with correctly interpolated frontmatter variables in markdown and HTML. @@ -19,6 +19,7 @@ * pages/ * blog/ * first-post.md + * second-post.md * layouts/ * blog.html */ @@ -51,7 +52,7 @@ describe('Build Greenwood With: ', function() { runner.runCommand(cliPath, 'build'); }); - describe('Frontmatter should be interpolated in the correct places', function() { + describe('Simple frontmatter should be interpolated in the correct places', function() { let dom; before(async function() { @@ -76,6 +77,28 @@ describe('Build Greenwood With: ', function() { expect(heading).to.be.equal('Author: Owen Buckley'); }); }); + + describe('Rich frontmatter should be interpolated in the correct places', function() { + let dom; + + before(async function() { + dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './blog/second-post/index.html')); + }); + + it('should have the correct songs frontmatter data in the page output', function() { + const contents = dom.window.document.querySelector('body span').innerHTML; + const songs = JSON.parse(contents); + + expect(songs.length).to.equal(2); + + songs.forEach((song, idx) => { + const num = idx += 1; + + expect(song.title).to.equal(`Song ${num}`); + expect(song.url).to.equal(`song${num}.mp3`); + }); + }); + }); }); after(function() { diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md b/packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md new file mode 100644 index 000000000..b6baed550 --- /dev/null +++ b/packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md @@ -0,0 +1,17 @@ +--- +title: Ny First Post +layout: blog +published: 11/11/2022 +author: Owen Buckley +songs: + - title: Song 1 + url: song1.mp3 + - title: Song 2 + url: song2.mp3 +--- + +# My Second Post + +## Playlist + +${globalThis.page.songs} \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js b/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js new file mode 100644 index 000000000..e1ddad6da --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js @@ -0,0 +1,172 @@ +/* + * Use Case + * Run Greenwood build command with prerender config set to true and using various content as data APIs. + * + * User Result + * Should generate a Greenwood build with the expected generated output using custom elements. + * + * User Command + * greenwood build + * + * User Config + * { + * prerender: true + * } + * + * User Workspace + * src/ + * components/ + * blog-posts-lists.js + * header.js + * toc.js + * pages/ + * blog/ + * first-post.md + * second-post.md + * index.html + * index.html + * toc.html + */ + +import chai from 'chai'; +import { JSDOM } from 'jsdom'; +import path from 'path'; +import { runSmokeTest } from '../../../../../test/smoke-test.js'; +import { getSetupFiles, getOutputTeardownFiles, getDependencyFiles } from '../../../../../test/utils.js'; +import { Runner } from 'gallinago'; +import { fileURLToPath, URL } from 'url'; + +const expect = chai.expect; + +describe('Build Greenwood With: ', function() { + const LABEL = 'Prerender Configuration turned on using collections'; + const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); + const outputPath = fileURLToPath(new URL('.', import.meta.url)); + let runner; + + before(function() { + this.context = { + publicDir: path.join(outputPath, 'public') + }; + runner = new Runner(); + }); + + describe(LABEL, function() { + + before(async function() { + const greenwoodDataLibs = await getDependencyFiles( + `${process.cwd()}/packages/cli/src/data/queries.js`, + `${outputPath}/node_modules/@greenwood/cli/src/data` + ); + + /* + * need a workaround here or else we get a module loader error + * ``` + * import { getContentByCollection } from "@greenwood/cli/src/data/queries.js"; + * ^^^^^^ + * SyntaxError: Named export 'getContentByCollection' not found. The requested module '@greenwood/cli/src/data/queries.js' + * is a CommonJS module, which may not support all module.exports as named exports. + * ``` + * + * but it works fine IRL - https://github.com/ProjectEvergreen/www.greenwoodjs.dev/pull/1 + */ + const packageJson = await getDependencyFiles( + `${outputPath}/package.json`, + `${outputPath}/node_modules/@greenwood/cli/` + ); + + runner.setup(outputPath, [ + ...getSetupFiles(outputPath), + ...greenwoodDataLibs, + ...packageJson + ]); + runner.runCommand(cliPath, 'build'); + }); + + runSmokeTest(['public', 'index'], LABEL); + + describe('Default output for index.html with nav collection content', function() { + let dom; + + before(async function() { + dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './index.html')); + }); + + describe('navigation links from getContentByCollection', function() { + let navLinks; + + before(function() { + navLinks = dom.window.document.querySelectorAll('header nav ul li a'); + }); + + it('should have the expected number of nav links from all pages in the collection', function() { + expect(navLinks.length).to.equal(3); + }); + + it('should have the expected link content from all pages in the collection', function() { + expect(navLinks[0].getAttribute('href')).to.equal('/'); + expect(navLinks[0].textContent).to.equal('Index'); + + expect(navLinks[1].getAttribute('href')).to.equal('/blog/'); + expect(navLinks[1].textContent).to.equal('Index'); + + expect(navLinks[2].getAttribute('href')).to.equal('/toc/'); + expect(navLinks[2].textContent).to.equal('Toc'); + }); + }); + }); + + describe('Default output for blog/index.html with routes based collection content', function() { + let dom; + + before(async function() { + dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './blog/index.html')); + }); + + describe('navigation links from getContentByCollection', function() { + let postLinks; + + before(function() { + postLinks = dom.window.document.querySelectorAll('ol li a'); + }); + + it('should have the expected number of post links from all blog pages in the collection (minus the index route)', function() { + expect(postLinks.length).to.equal(2); + }); + + it('should have the expected link content from all pages in the collection', function() { + expect(postLinks[0].getAttribute('href')).to.equal('/blog/first-post/'); + expect(postLinks[0].textContent).to.equal('First Post'); + + expect(postLinks[1].getAttribute('href')).to.equal('/blog/second-post/'); + expect(postLinks[1].textContent).to.equal('Second Post'); + }); + }); + }); + + describe('Default output for toc.html with all content in a list', function() { + let dom; + + before(async function() { + dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './toc/index.html')); + }); + + describe('navigation links from getContentByCollection', function() { + let pageLinks; + + before(function() { + pageLinks = dom.window.document.querySelectorAll('ol li a'); + }); + + // includes 404 page + it('should have the expected number of post links from all blog pages in the collection (minus the index route)', function() { + expect(pageLinks.length).to.equal(6); + }); + }); + }); + }); + + after(async function() { + runner.teardown(getOutputTeardownFiles(outputPath)); + }); +}); \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/greenwood.config.js b/packages/cli/test/cases/build.config.prerender-collections/greenwood.config.js new file mode 100644 index 000000000..8dc4be464 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/greenwood.config.js @@ -0,0 +1,3 @@ +export default { + prerender: true +}; \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/package.json b/packages/cli/test/cases/build.config.prerender-collections/package.json new file mode 100644 index 000000000..aead43de3 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/components/blog-posts-list.js b/packages/cli/test/cases/build.config.prerender-collections/src/components/blog-posts-list.js new file mode 100644 index 000000000..a5ffebb4c --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/components/blog-posts-list.js @@ -0,0 +1,26 @@ +import { getContentByRoute } from '@greenwood/cli/src/data/queries.js'; + +export default class BlogPostsList extends HTMLElement { + async connectedCallback() { + const posts = (await getContentByRoute('/blog')).filter( + (page) => page.label !== 'Index' && page.label !== 'Blog' + ); + + this.innerHTML = ` +
    + ${ + posts.map((post) => { + const { label, route } = post; + return ` +
  1. + ${label} +
  2. + `; + }).join('') + } +
+ `; + } +} + +customElements.define('x-posts-list', BlogPostsList); \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/components/header.js b/packages/cli/test/cases/build.config.prerender-collections/src/components/header.js new file mode 100644 index 000000000..f7da915a9 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/components/header.js @@ -0,0 +1,29 @@ +import { getContentByCollection } from '@greenwood/cli/src/data/queries.js'; + +export default class Header extends HTMLElement { + async connectedCallback() { + const navItems = (await getContentByCollection('nav')).sort((a, b) => + a.data.order > b.data.order ? 1 : -1 + ); + + this.innerHTML = ` +
+ +
+ `; + } +} + +customElements.define('x-header', Header); \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/components/toc.js b/packages/cli/test/cases/build.config.prerender-collections/src/components/toc.js new file mode 100644 index 000000000..86a80030e --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/components/toc.js @@ -0,0 +1,24 @@ +import { getContent } from '@greenwood/cli/src/data/queries.js'; + +export default class ToC extends HTMLElement { + async connectedCallback() { + const pages = await getContent(); + + this.innerHTML = ` +
    + ${ + pages.map((page) => { + const { label, route } = page; + return ` +
  1. + ${label} +
  2. + `; + }).join('') + } +
+ `; + } +} + +customElements.define('x-toc', ToC); \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/first-post.md b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/first-post.md new file mode 100644 index 000000000..2f41b6873 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/first-post.md @@ -0,0 +1,3 @@ +# First Post + +Lorum Ipsum \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/index.html b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/index.html new file mode 100644 index 000000000..016dc2b19 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/index.html @@ -0,0 +1,14 @@ +--- +collection: nav +order: 2 +--- + + + + + + +

Blog Posts

+ + + \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/second-post.md b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/second-post.md new file mode 100644 index 000000000..ba4dc1e04 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/blog/second-post.md @@ -0,0 +1,3 @@ +# Second Post + +Lorum Ipsum \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/index.html b/packages/cli/test/cases/build.config.prerender-collections/src/pages/index.html new file mode 100644 index 000000000..53c05e3c4 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/index.html @@ -0,0 +1,14 @@ +--- +collection: nav +order: 1 +--- + + + + + + + +

Home Page

+ + \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html b/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html new file mode 100644 index 000000000..ae18fb8f3 --- /dev/null +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html @@ -0,0 +1,14 @@ +--- +collection: nav +order: 3 +--- + + + + + + +

Table of Contents Page

+ + + \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.prerender/build.config.prerender.spec.js b/packages/cli/test/cases/build.config.prerender/build.config.prerender.spec.js index 881d19886..bd9626299 100644 --- a/packages/cli/test/cases/build.config.prerender/build.config.prerender.spec.js +++ b/packages/cli/test/cases/build.config.prerender/build.config.prerender.spec.js @@ -3,7 +3,7 @@ * Run Greenwood build command with prerender config set to true. * * User Result - * Should generate a Greenwood build with puppeteer generated output for Web Components. + * Should generate a Greenwood build with the expected generated output using custom elements. * * User Command * greenwood build From 331bce1c0fecd3fc5720422572235f1c1b6cceb2 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 24 Aug 2024 14:09:22 -0400 Subject: [PATCH 03/36] rename interpolateFrontmatter to activeFrontmatter --- greenwood.config.js | 2 +- packages/cli/src/lib/layout-utils.js | 4 ++-- packages/cli/src/lifecycles/config.js | 13 ++++++------- .../src/plugins/resource/plugin-standard-html.js | 4 ++-- .../build.config.active-frontmatter.spec.js} | 6 +++--- .../greenwood.config.js | 3 +++ .../src/layouts/blog.html | 0 .../src/pages/blog/first-post.md | 0 .../src/pages/blog/second-post.md | 0 .../greenwood.config.js | 3 --- www/pages/docs/configuration.md | 6 +++--- 11 files changed, 20 insertions(+), 21 deletions(-) rename packages/cli/test/cases/{build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js => build.config.active-frontmatter/build.config.active-frontmatter.spec.js} (94%) create mode 100644 packages/cli/test/cases/build.config.active-frontmatter/greenwood.config.js rename packages/cli/test/cases/{build.config.interpolate-frontmatter => build.config.active-frontmatter}/src/layouts/blog.html (100%) rename packages/cli/test/cases/{build.config.interpolate-frontmatter => build.config.active-frontmatter}/src/pages/blog/first-post.md (100%) rename packages/cli/test/cases/{build.config.interpolate-frontmatter => build.config.active-frontmatter}/src/pages/blog/second-post.md (100%) delete mode 100644 packages/cli/test/cases/build.config.interpolate-frontmatter/greenwood.config.js diff --git a/greenwood.config.js b/greenwood.config.js index 216952137..c01234a7a 100644 --- a/greenwood.config.js +++ b/greenwood.config.js @@ -10,7 +10,7 @@ export default { workspace: new URL('./www/', import.meta.url), optimization: 'inline', staticRouter: true, - interpolateFrontmatter: true, + activeFrontmatter: true, plugins: [ greenwoodPluginGraphQL(), greenwoodPluginPolyfills({ diff --git a/packages/cli/src/lib/layout-utils.js b/packages/cli/src/lib/layout-utils.js index 8dbf28197..d52a6c214 100644 --- a/packages/cli/src/lib/layout-utils.js +++ b/packages/cli/src/lib/layout-utils.js @@ -193,10 +193,10 @@ async function getAppLayout(pageLayoutContents, compilation, customImports = [], const appBody = appRoot.querySelector('body') ? appRoot.querySelector('body').innerHTML : ''; const pageBody = pageRoot && pageRoot.querySelector('body') ? pageRoot.querySelector('body').innerHTML : ''; const pageTitle = pageRoot && pageRoot.querySelector('head title'); - const hasInterpolatedFrontmatter = pageTitle && pageTitle.rawText.indexOf('${globalThis.page.title}') >= 0 + const hasActiveFrontmatterTitle = pageTitle && pageTitle.rawText.indexOf('${globalThis.page.title}') >= 0 || appTitle && appTitle.rawText.indexOf('${globalThis.page.title}') >= 0; - const title = hasInterpolatedFrontmatter // favor frontmatter interpolation first + const title = hasActiveFrontmatterTitle // favor active frontmatter title first ? pageTitle && pageTitle.rawText ? pageTitle.rawText : appTitle.rawText diff --git a/packages/cli/src/lifecycles/config.js b/packages/cli/src/lifecycles/config.js index 9c31a230a..d0883d84f 100644 --- a/packages/cli/src/lifecycles/config.js +++ b/packages/cli/src/lifecycles/config.js @@ -46,7 +46,7 @@ const defaultConfig = { port: 8080, basePath: '', optimization: optimizations[0], - interpolateFrontmatter: false, + activeFrontmatter: false, plugins: greenwoodPlugins, markdown: { plugins: [], settings: {} }, prerender: false, @@ -81,8 +81,7 @@ const readAndMergeConfig = async() => { if (hasConfigFile) { const userCfgFile = (await import(configUrl)).default; - // eslint-disable-next-line max-len - const { workspace, devServer, markdown, optimization, plugins, port, prerender, basePath, staticRouter, pagesDirectory, layoutsDirectory, interpolateFrontmatter, isolation, polyfills } = userCfgFile; + const { workspace, devServer, markdown, optimization, plugins, port, prerender, basePath, staticRouter, pagesDirectory, layoutsDirectory, activeFrontmatter, isolation, polyfills } = userCfgFile; // workspace validation if (workspace) { @@ -103,11 +102,11 @@ const readAndMergeConfig = async() => { reject(`Error: provided optimization "${optimization}" is not supported. Please use one of: ${optimizations.join(', ')}.`); } - if (interpolateFrontmatter) { - if (typeof interpolateFrontmatter !== 'boolean') { - reject('Error: greenwood.config.js interpolateFrontmatter must be a boolean'); + if (activeFrontmatter) { + if (typeof activeFrontmatter !== 'boolean') { + reject('Error: greenwood.config.js activeFrontmatter must be a boolean'); } - customConfig.interpolateFrontmatter = interpolateFrontmatter; + customConfig.activeFrontmatter = activeFrontmatter; } if (plugins && plugins.length > 0) { diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 0f07495d9..6650bea29 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -38,7 +38,7 @@ class StandardHtmlResource extends ResourceInterface { async serve(url, request) { const { config, context } = this.compilation; const { pagesDir, userWorkspace } = context; - const { interpolateFrontmatter } = config; + const { activeFrontmatter } = config; const { pathname } = url; const isSpaRoute = this.compilation.graph.find(node => node.isSPA); const matchingRoute = this.compilation.graph.find((node) => node.route === pathname) || {}; @@ -171,7 +171,7 @@ class StandardHtmlResource extends ResourceInterface { body = body.replace(/\(.*)<\/content-outlet>/s, `${ssrBody.replace(/\$/g, '$$$')}`); } - if (interpolateFrontmatter) { + if (activeFrontmatter) { console.log({ frontMatter, matchingRoute }); // TODO consolidate this for (const fm in matchingRoute.data) { diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js b/packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js similarity index 94% rename from packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js rename to packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js index 544fabe83..b993d71e0 100644 --- a/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js +++ b/packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js @@ -1,6 +1,6 @@ /* * Use Case - * Run Greenwood with interpolateFrontmatter configuration enabled for simple and rich frontmatter. + * Run Greenwood with activeFrontmatter configuration enabled for simple and rich frontmatter. * * User Result * Should generate a bare bones Greenwood build with correctly interpolated frontmatter variables in markdown and HTML. @@ -10,7 +10,7 @@ * * User Config * { - * interpolateFrontmatter: true + * activeFrontmatter: true * } * * User Workspace @@ -33,7 +33,7 @@ import { fileURLToPath, URL } from 'url'; const expect = chai.expect; describe('Build Greenwood With: ', function() { - const LABEL = 'Frontmatter Interpolation'; + const LABEL = 'Active Frontmatter'; const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); const outputPath = fileURLToPath(new URL('.', import.meta.url)); let runner; diff --git a/packages/cli/test/cases/build.config.active-frontmatter/greenwood.config.js b/packages/cli/test/cases/build.config.active-frontmatter/greenwood.config.js new file mode 100644 index 000000000..34dfe08c1 --- /dev/null +++ b/packages/cli/test/cases/build.config.active-frontmatter/greenwood.config.js @@ -0,0 +1,3 @@ +export default { + activeFrontmatter: true +}; \ No newline at end of file diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/src/layouts/blog.html b/packages/cli/test/cases/build.config.active-frontmatter/src/layouts/blog.html similarity index 100% rename from packages/cli/test/cases/build.config.interpolate-frontmatter/src/layouts/blog.html rename to packages/cli/test/cases/build.config.active-frontmatter/src/layouts/blog.html diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/first-post.md b/packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/first-post.md similarity index 100% rename from packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/first-post.md rename to packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/first-post.md diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md b/packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/second-post.md similarity index 100% rename from packages/cli/test/cases/build.config.interpolate-frontmatter/src/pages/blog/second-post.md rename to packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/second-post.md diff --git a/packages/cli/test/cases/build.config.interpolate-frontmatter/greenwood.config.js b/packages/cli/test/cases/build.config.interpolate-frontmatter/greenwood.config.js deleted file mode 100644 index 41bc6bc88..000000000 --- a/packages/cli/test/cases/build.config.interpolate-frontmatter/greenwood.config.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - interpolateFrontmatter: true -}; \ No newline at end of file diff --git a/www/pages/docs/configuration.md b/www/pages/docs/configuration.md index 6779cc57d..4f08e8086 100644 --- a/www/pages/docs/configuration.md +++ b/www/pages/docs/configuration.md @@ -20,7 +20,7 @@ export default { }, basePath: '', port: 8080, - interpolateFrontmatter: false, + activeFrontmatter: false, markdown: { plugins: [], settings: {} @@ -86,9 +86,9 @@ export default { }; ``` -### Interpolate Frontmatter +### Active Frontmatter -To support simple static templating in HTML and markdown pages and layouts, the `interpolateFrontmatter` option can be set to `true` to allow the following kinds of simple static substitutions using a syntax convention based on JavaScript [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). +To support simple static templating in HTML and markdown pages and layouts, the `activeFrontmatter` option can be set to `true` to allow the following kinds of simple static substitutions using a syntax convention based on JavaScript [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). #### Example Given some frontmatter in a markdown file: From 6cf8dc64781eb740faae6968ece12e7fc10ac38d Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 24 Aug 2024 19:23:53 -0400 Subject: [PATCH 04/36] refactor id and lable graph properties --- packages/cli/src/lifecycles/graph.js | 43 ++++++++++++------- ...build.config.prerender-collections.spec.js | 8 ++-- .../src/pages/toc.html | 1 + .../cases/query-graph/query-graph.spec.js | 2 +- www/pages/about/community.md | 1 - www/pages/about/features.md | 1 - www/pages/about/goals.md | 1 - www/pages/about/how-it-works.md | 1 - www/pages/about/index.md | 1 - www/pages/about/tech-stack.md | 1 - www/pages/blog/index.md | 1 - www/pages/blog/release/v0-15-0.md | 1 - www/pages/blog/release/v0-18-0.md | 1 - www/pages/blog/release/v0-19-0.md | 1 - www/pages/blog/release/v0-20-0.md | 1 - www/pages/blog/release/v0-21-0.md | 1 - www/pages/blog/release/v0-23-0.md | 1 - www/pages/blog/release/v0-24-0.md | 1 - www/pages/blog/release/v0-26-0.md | 1 - www/pages/blog/release/v0-27-0.md | 1 - www/pages/blog/release/v0-28-0.md | 1 - www/pages/blog/release/v0-29-0.md | 1 - www/pages/blog/state-of-greenwood-2022.md | 1 - www/pages/blog/state-of-greenwood-2023.md | 1 - www/pages/docs/api-routes.md | 2 +- www/pages/docs/component-model.md | 1 - www/pages/docs/configuration.md | 1 - www/pages/docs/css-and-images.md | 2 +- www/pages/docs/data.md | 2 +- www/pages/docs/front-matter.md | 1 - www/pages/docs/index.md | 1 - www/pages/docs/layouts.md | 2 +- www/pages/docs/markdown.md | 1 - www/pages/docs/menus.md | 1 - www/pages/docs/scripts.md | 2 +- www/pages/docs/server-rendering.md | 1 - www/pages/getting-started/branding.md | 2 +- www/pages/getting-started/build-and-deploy.md | 1 - www/pages/getting-started/creating-content.md | 1 - www/pages/getting-started/index.md | 1 - www/pages/getting-started/key-concepts.md | 1 - www/pages/getting-started/next-steps.md | 1 - www/pages/getting-started/optimizing.md | 1 - www/pages/getting-started/project-setup.md | 1 - www/pages/getting-started/quick-start.md | 1 - www/pages/guides/index.md | 1 - www/pages/guides/netlify-cms.md | 1 + www/pages/plugins/adapter.md | 1 - www/pages/plugins/context.md | 1 - www/pages/plugins/copy.md | 1 - www/pages/plugins/custom-plugins.md | 1 - www/pages/plugins/index.md | 1 - www/pages/plugins/renderer.md | 1 - www/pages/plugins/resource.md | 1 - www/pages/plugins/rollup.md | 1 - www/pages/plugins/server.md | 1 - www/pages/plugins/source.md | 1 - 57 files changed, 41 insertions(+), 72 deletions(-) diff --git a/packages/cli/src/lifecycles/graph.js b/packages/cli/src/lifecycles/graph.js index 68c8a1989..33fd5898d 100644 --- a/packages/cli/src/lifecycles/graph.js +++ b/packages/cli/src/lifecycles/graph.js @@ -5,6 +5,25 @@ import { checkResourceExists, requestAsObject } from '../lib/resource-utils.js'; import toc from 'markdown-toc'; import { Worker } from 'worker_threads'; +function labelFromRoute(_route) { + let route = _route; + + if (route === '/index/') { + return 'Home'; + } else if (route.endsWith('/index/')) { + route = route.replace('index/', ''); + } + + return route + .split('/') + .filter(part => part !== '') + .pop() + .split('-') + .map((routePart) => { + return `${routePart.charAt(0).toUpperCase()}${routePart.substring(1)}`; + }) + .join(' '); +} const generateGraph = async (compilation) => { return new Promise(async (resolve, reject) => { @@ -98,9 +117,10 @@ const generateGraph = async (compilation) => { }); } else if (isPage) { let route = relativePagePath.replace(extension, ''); - let id = filename.split('/')[filename.split('/').length - 1].replace(extension, ''); + let root = filename.split('/')[filename.split('/').length - 1].replace(extension, ''); let layout = extension === '.html' ? null : 'page'; - let title = null; + let label = labelFromRoute(`${route}/`); + let title = null; // TODO use label here let imports = []; let customData = {}; let filePath; @@ -118,7 +138,7 @@ const generateGraph = async (compilation) => { */ if (relativePagePath.lastIndexOf('/') > 0) { // https://github.com/ProjectEvergreen/greenwood/issues/455 - route = id === 'index' || route.replace('/index', '') === `/${id}` + route = root === 'index' || route.replace('/index', '') === `/${root}` ? route.replace('index', '') : `${route}/`; } else { @@ -133,7 +153,7 @@ const generateGraph = async (compilation) => { layout = attributes.layout || layout; title = attributes.title || title; - id = attributes.label || id; + label = attributes.label || label; imports = attributes.imports || []; filePath = `${relativeWorkspacePath}${filename}`; @@ -214,11 +234,8 @@ const generateGraph = async (compilation) => { page: JSON.stringify({ servePage: isCustom, route, - id, - label: id.split('-') - .map((idPart) => { - return `${idPart.charAt(0).toUpperCase()}${idPart.substring(1)}`; - }).join(' ') + root, + label }), request }); @@ -229,6 +246,7 @@ const generateGraph = async (compilation) => { title = ssrFrontmatter.title || title; imports = ssrFrontmatter.imports || imports; customData = ssrFrontmatter.data || customData; + label = ssrFrontmatter.label || label; /* Menu Query * Custom front matter - Variable Definitions @@ -248,7 +266,6 @@ const generateGraph = async (compilation) => { *---------------------- * data: custom page frontmatter * filename: base filename of the page - * id: filename without the extension * relativeWorkspacePagePath: the file path relative to the user's workspace directory * label: "pretty" text representation of the filename * imports: per page JS or CSS file imports to be included in HTML output from frontmatter @@ -267,12 +284,8 @@ const generateGraph = async (compilation) => { const page = { data: customData || {}, filename, - id, relativeWorkspacePagePath: relativePagePath, - label: id.split('-') - .map((idPart) => { - return `${idPart.charAt(0).toUpperCase()}${idPart.substring(1)}`; - }).join(' '), + label, imports, resources: [], outputPath: route === '/404/' diff --git a/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js b/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js index e1ddad6da..d2d59da5b 100644 --- a/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js +++ b/packages/cli/test/cases/build.config.prerender-collections/build.config.prerender-collections.spec.js @@ -85,7 +85,7 @@ describe('Build Greenwood With: ', function() { runSmokeTest(['public', 'index'], LABEL); - describe('Default output for index.html with nav collection content', function() { + describe('Default output for index.html with header nav collection content', function() { let dom; before(async function() { @@ -105,13 +105,13 @@ describe('Build Greenwood With: ', function() { it('should have the expected link content from all pages in the collection', function() { expect(navLinks[0].getAttribute('href')).to.equal('/'); - expect(navLinks[0].textContent).to.equal('Index'); + expect(navLinks[0].textContent).to.equal('Home'); expect(navLinks[1].getAttribute('href')).to.equal('/blog/'); - expect(navLinks[1].textContent).to.equal('Index'); + expect(navLinks[1].textContent).to.equal('Blog'); expect(navLinks[2].getAttribute('href')).to.equal('/toc/'); - expect(navLinks[2].textContent).to.equal('Toc'); + expect(navLinks[2].textContent).to.equal('Table of Contents'); }); }); }); diff --git a/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html b/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html index ae18fb8f3..dea911cd1 100644 --- a/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html +++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html @@ -1,6 +1,7 @@ --- collection: nav order: 3 +label: Table of Contents --- diff --git a/packages/plugin-graphql/test/cases/query-graph/query-graph.spec.js b/packages/plugin-graphql/test/cases/query-graph/query-graph.spec.js index 11209a801..e0fd838e4 100644 --- a/packages/plugin-graphql/test/cases/query-graph/query-graph.spec.js +++ b/packages/plugin-graphql/test/cases/query-graph/query-graph.spec.js @@ -192,7 +192,7 @@ describe('Build Greenwood With: ', function() { expect(listItems[0].innerHTML).to.contain('First Post'); expect(listItems[1].innerHTML).to.contain('Second Post'); - expect(listItems[2].innerHTML).to.contain('Index'); + expect(listItems[2].innerHTML).to.contain('Home'); expect(listItems[3].innerHTML).to.contain('Not Found'); }); }); diff --git a/www/pages/about/community.md b/www/pages/about/community.md index 30b02970f..829ea6b0e 100644 --- a/www/pages/about/community.md +++ b/www/pages/about/community.md @@ -1,5 +1,4 @@ --- -label: 'community' menu: side title: 'Community' index: 3 diff --git a/www/pages/about/features.md b/www/pages/about/features.md index 83b92eb47..4c3f51bbb 100644 --- a/www/pages/about/features.md +++ b/www/pages/about/features.md @@ -1,5 +1,4 @@ --- -label: 'features' menu: side title: 'Features' index: 2 diff --git a/www/pages/about/goals.md b/www/pages/about/goals.md index a5daf908a..006e8ff19 100644 --- a/www/pages/about/goals.md +++ b/www/pages/about/goals.md @@ -1,5 +1,4 @@ --- -label: 'goals' menu: side title: 'Goals' index: 0 diff --git a/www/pages/about/how-it-works.md b/www/pages/about/how-it-works.md index 1a793e04d..a3404e500 100644 --- a/www/pages/about/how-it-works.md +++ b/www/pages/about/how-it-works.md @@ -1,5 +1,4 @@ --- -label: 'how-it-works' menu: side title: 'How It Works' index: 1 diff --git a/www/pages/about/index.md b/www/pages/about/index.md index d02500819..488169132 100644 --- a/www/pages/about/index.md +++ b/www/pages/about/index.md @@ -1,5 +1,4 @@ --- -label: 'about' menu: navigation title: About index: 1 diff --git a/www/pages/about/tech-stack.md b/www/pages/about/tech-stack.md index bc327d317..80e23c248 100644 --- a/www/pages/about/tech-stack.md +++ b/www/pages/about/tech-stack.md @@ -1,5 +1,4 @@ --- -label: 'tech-stack' menu: side title: 'Tech Stack' index: 4 diff --git a/www/pages/blog/index.md b/www/pages/blog/index.md index 65fcb7eeb..42ef6ad83 100644 --- a/www/pages/blog/index.md +++ b/www/pages/blog/index.md @@ -1,5 +1,4 @@ --- -label: 'blog' menu: navigation title: Blog index: 6 diff --git a/www/pages/blog/release/v0-15-0.md b/www/pages/blog/release/v0-15-0.md index 9b87d521e..1c504aa9c 100644 --- a/www/pages/blog/release/v0-15-0.md +++ b/www/pages/blog/release/v0-15-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.15.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-18-0.md b/www/pages/blog/release/v0-18-0.md index b9c55de1c..8dbe34ecb 100644 --- a/www/pages/blog/release/v0-18-0.md +++ b/www/pages/blog/release/v0-18-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.18.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-19-0.md b/www/pages/blog/release/v0-19-0.md index 63a0f4e10..f653943f8 100644 --- a/www/pages/blog/release/v0-19-0.md +++ b/www/pages/blog/release/v0-19-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.19.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-20-0.md b/www/pages/blog/release/v0-20-0.md index 2d20f4173..84a6b16b7 100644 --- a/www/pages/blog/release/v0-20-0.md +++ b/www/pages/blog/release/v0-20-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.20.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-21-0.md b/www/pages/blog/release/v0-21-0.md index 5a367222b..8c2c6d3f1 100644 --- a/www/pages/blog/release/v0-21-0.md +++ b/www/pages/blog/release/v0-21-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.21.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-23-0.md b/www/pages/blog/release/v0-23-0.md index a7d00b1fd..78d9eb7f8 100644 --- a/www/pages/blog/release/v0-23-0.md +++ b/www/pages/blog/release/v0-23-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.23.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-24-0.md b/www/pages/blog/release/v0-24-0.md index 2456e09fd..f8a53f456 100644 --- a/www/pages/blog/release/v0-24-0.md +++ b/www/pages/blog/release/v0-24-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.24.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-26-0.md b/www/pages/blog/release/v0-26-0.md index 2846b4406..d2837afbe 100644 --- a/www/pages/blog/release/v0-26-0.md +++ b/www/pages/blog/release/v0-26-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.26.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-27-0.md b/www/pages/blog/release/v0-27-0.md index 3b31409fe..df2ef0b57 100644 --- a/www/pages/blog/release/v0-27-0.md +++ b/www/pages/blog/release/v0-27-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.27.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-28-0.md b/www/pages/blog/release/v0-28-0.md index b82498c34..472d63100 100644 --- a/www/pages/blog/release/v0-28-0.md +++ b/www/pages/blog/release/v0-28-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.28.0 Release layout: blog --- diff --git a/www/pages/blog/release/v0-29-0.md b/www/pages/blog/release/v0-29-0.md index 0fb29a2e7..258fda541 100644 --- a/www/pages/blog/release/v0-29-0.md +++ b/www/pages/blog/release/v0-29-0.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: v0.29.0 Release layout: blog --- diff --git a/www/pages/blog/state-of-greenwood-2022.md b/www/pages/blog/state-of-greenwood-2022.md index ec60ba169..63c3ef8d8 100644 --- a/www/pages/blog/state-of-greenwood-2022.md +++ b/www/pages/blog/state-of-greenwood-2022.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: State of Greenwood (2022) layout: blog --- diff --git a/www/pages/blog/state-of-greenwood-2023.md b/www/pages/blog/state-of-greenwood-2023.md index 8f7d21c98..9642ae41b 100644 --- a/www/pages/blog/state-of-greenwood-2023.md +++ b/www/pages/blog/state-of-greenwood-2023.md @@ -1,5 +1,4 @@ --- -label: 'blog' title: State of Greenwood (2023) layout: blog --- diff --git a/www/pages/docs/api-routes.md b/www/pages/docs/api-routes.md index 7edc0d617..2c3d52bdf 100644 --- a/www/pages/docs/api-routes.md +++ b/www/pages/docs/api-routes.md @@ -1,5 +1,5 @@ --- -label: 'API-routes' +label: 'API Routes' menu: side title: 'API Routes' index: 9 diff --git a/www/pages/docs/component-model.md b/www/pages/docs/component-model.md index f06e01000..f472c3463 100644 --- a/www/pages/docs/component-model.md +++ b/www/pages/docs/component-model.md @@ -1,5 +1,4 @@ --- -label: 'component-model' menu: side title: 'Component Model' index: 1 diff --git a/www/pages/docs/configuration.md b/www/pages/docs/configuration.md index 4f08e8086..966e0a3d8 100644 --- a/www/pages/docs/configuration.md +++ b/www/pages/docs/configuration.md @@ -1,5 +1,4 @@ --- -label: 'configuration' menu: side title: 'Configuration' index: 2 diff --git a/www/pages/docs/css-and-images.md b/www/pages/docs/css-and-images.md index 2b450e4fb..9d7e2afe7 100644 --- a/www/pages/docs/css-and-images.md +++ b/www/pages/docs/css-and-images.md @@ -1,5 +1,5 @@ --- -label: 'styles-and-assets' +label: 'Styles and Assets' menu: side title: 'Styles and Assets' index: 6 diff --git a/www/pages/docs/data.md b/www/pages/docs/data.md index 881b6822a..ca8c58477 100644 --- a/www/pages/docs/data.md +++ b/www/pages/docs/data.md @@ -1,5 +1,5 @@ --- -label: 'data-sources' +label: 'Data Sources' menu: side title: 'Data Sources' index: 11 diff --git a/www/pages/docs/front-matter.md b/www/pages/docs/front-matter.md index c86114135..906673c41 100644 --- a/www/pages/docs/front-matter.md +++ b/www/pages/docs/front-matter.md @@ -1,5 +1,4 @@ --- -label: 'front-matter' menu: side title: 'Front Matter' index: 3 diff --git a/www/pages/docs/index.md b/www/pages/docs/index.md index 9ae7e0dad..8c1a8d3fc 100644 --- a/www/pages/docs/index.md +++ b/www/pages/docs/index.md @@ -1,5 +1,4 @@ --- -label: 'docs' menu: navigation title: Docs index: 2 diff --git a/www/pages/docs/layouts.md b/www/pages/docs/layouts.md index 4f863b030..c7c61335e 100644 --- a/www/pages/docs/layouts.md +++ b/www/pages/docs/layouts.md @@ -1,5 +1,5 @@ --- -label: 'layouts-and-pages' +label: 'Layouts and Pages' menu: side title: 'Layouts and Pages' index: 7 diff --git a/www/pages/docs/markdown.md b/www/pages/docs/markdown.md index 05e1b8c38..85cdc73c4 100644 --- a/www/pages/docs/markdown.md +++ b/www/pages/docs/markdown.md @@ -1,5 +1,4 @@ --- -label: 'markdown' menu: side title: 'Markdown' index: 4 diff --git a/www/pages/docs/menus.md b/www/pages/docs/menus.md index bd473da0e..20743d42c 100644 --- a/www/pages/docs/menus.md +++ b/www/pages/docs/menus.md @@ -1,5 +1,4 @@ --- -label: 'menus' menu: side title: 'Menus' index: 10 diff --git a/www/pages/docs/scripts.md b/www/pages/docs/scripts.md index 3c94461e5..02175be22 100644 --- a/www/pages/docs/scripts.md +++ b/www/pages/docs/scripts.md @@ -1,5 +1,5 @@ --- -label: 'scripts-and-imports' +label: 'Scripts and Imports' menu: side title: 'Scripts and Imports' index: 5 diff --git a/www/pages/docs/server-rendering.md b/www/pages/docs/server-rendering.md index a32e0ea90..e261d27bb 100644 --- a/www/pages/docs/server-rendering.md +++ b/www/pages/docs/server-rendering.md @@ -1,5 +1,4 @@ --- -label: 'server-rendering' menu: side title: 'Server Rendering' index: 8 diff --git a/www/pages/getting-started/branding.md b/www/pages/getting-started/branding.md index d92f39aa4..f83dd4e8d 100644 --- a/www/pages/getting-started/branding.md +++ b/www/pages/getting-started/branding.md @@ -1,5 +1,5 @@ --- -label: 'components-and-styles' +label: 'Components and Styles' menu: side title: 'Components and Styles' index: 5 diff --git a/www/pages/getting-started/build-and-deploy.md b/www/pages/getting-started/build-and-deploy.md index 852821b34..262079005 100644 --- a/www/pages/getting-started/build-and-deploy.md +++ b/www/pages/getting-started/build-and-deploy.md @@ -1,5 +1,4 @@ --- -label: 'build-and-deploy' menu: side title: 'Build and Deploy' index: 6 diff --git a/www/pages/getting-started/creating-content.md b/www/pages/getting-started/creating-content.md index 45cb6f428..83906c25e 100644 --- a/www/pages/getting-started/creating-content.md +++ b/www/pages/getting-started/creating-content.md @@ -1,5 +1,4 @@ --- -label: 'creating-content' menu: side title: 'Creating Content' index: 4 diff --git a/www/pages/getting-started/index.md b/www/pages/getting-started/index.md index 3aaff03f9..d8f0f5b7a 100644 --- a/www/pages/getting-started/index.md +++ b/www/pages/getting-started/index.md @@ -1,5 +1,4 @@ --- -label: 'getting-started' menu: navigation title: 'Getting Started' index: 3 diff --git a/www/pages/getting-started/key-concepts.md b/www/pages/getting-started/key-concepts.md index 10107ffee..9ebcab817 100644 --- a/www/pages/getting-started/key-concepts.md +++ b/www/pages/getting-started/key-concepts.md @@ -1,5 +1,4 @@ --- -label: 'key-concepts' menu: side title: 'Key Concepts' index: 3 diff --git a/www/pages/getting-started/next-steps.md b/www/pages/getting-started/next-steps.md index 4445e4aa8..c1e171cf6 100644 --- a/www/pages/getting-started/next-steps.md +++ b/www/pages/getting-started/next-steps.md @@ -1,5 +1,4 @@ --- -label: 'next-steps' menu: side title: 'Next Steps' index: 8 diff --git a/www/pages/getting-started/optimizing.md b/www/pages/getting-started/optimizing.md index d32b9423f..86f8b5727 100644 --- a/www/pages/getting-started/optimizing.md +++ b/www/pages/getting-started/optimizing.md @@ -1,5 +1,4 @@ --- -label: 'optimizing' menu: side title: 'Optimizing' index: 7 diff --git a/www/pages/getting-started/project-setup.md b/www/pages/getting-started/project-setup.md index 335f2a5a3..5d0049b6f 100644 --- a/www/pages/getting-started/project-setup.md +++ b/www/pages/getting-started/project-setup.md @@ -1,5 +1,4 @@ --- -label: 'project-setup' menu: side title: 'Project Setup' index: 2 diff --git a/www/pages/getting-started/quick-start.md b/www/pages/getting-started/quick-start.md index 3b05ed66a..48cada4b9 100644 --- a/www/pages/getting-started/quick-start.md +++ b/www/pages/getting-started/quick-start.md @@ -1,5 +1,4 @@ --- -label: 'quick-start' menu: side title: 'Quick Start' index: 1 diff --git a/www/pages/guides/index.md b/www/pages/guides/index.md index 8d67cb6d0..9b97f5e54 100644 --- a/www/pages/guides/index.md +++ b/www/pages/guides/index.md @@ -1,5 +1,4 @@ --- -label: 'guides' title: 'Guides' menu: navigation index: 5 diff --git a/www/pages/guides/netlify-cms.md b/www/pages/guides/netlify-cms.md index beaabd2b6..490793b02 100644 --- a/www/pages/guides/netlify-cms.md +++ b/www/pages/guides/netlify-cms.md @@ -1,4 +1,5 @@ --- +label: 'Netlify CMS' title: 'Netlify CMS' menu: side linkheadings: 3 diff --git a/www/pages/plugins/adapter.md b/www/pages/plugins/adapter.md index ebd0b8a31..406d7b36e 100644 --- a/www/pages/plugins/adapter.md +++ b/www/pages/plugins/adapter.md @@ -1,5 +1,4 @@ --- -label: 'adapter' menu: side title: 'Adapter' index: 1 diff --git a/www/pages/plugins/context.md b/www/pages/plugins/context.md index a995c2aa8..e464336b2 100644 --- a/www/pages/plugins/context.md +++ b/www/pages/plugins/context.md @@ -1,5 +1,4 @@ --- -label: 'context' menu: side title: 'Context' index: 2 diff --git a/www/pages/plugins/copy.md b/www/pages/plugins/copy.md index a2c904776..46c47ba56 100644 --- a/www/pages/plugins/copy.md +++ b/www/pages/plugins/copy.md @@ -1,5 +1,4 @@ --- -label: 'copy' menu: side title: 'Copy' index: 3 diff --git a/www/pages/plugins/custom-plugins.md b/www/pages/plugins/custom-plugins.md index 8be242786..dfe30ee3a 100644 --- a/www/pages/plugins/custom-plugins.md +++ b/www/pages/plugins/custom-plugins.md @@ -1,5 +1,4 @@ --- -label: 'custom-plugins' menu: side title: 'Custom Plugins' index: 9 diff --git a/www/pages/plugins/index.md b/www/pages/plugins/index.md index a9500ca43..58d58e97b 100644 --- a/www/pages/plugins/index.md +++ b/www/pages/plugins/index.md @@ -1,5 +1,4 @@ --- -label: 'plugins' menu: navigation title: Plugins index: 4 diff --git a/www/pages/plugins/renderer.md b/www/pages/plugins/renderer.md index 4b7f9825b..bfa734766 100644 --- a/www/pages/plugins/renderer.md +++ b/www/pages/plugins/renderer.md @@ -1,5 +1,4 @@ --- -label: 'Renderer' menu: side title: 'Renderer' index: 5 diff --git a/www/pages/plugins/resource.md b/www/pages/plugins/resource.md index fa671a772..a22cb3462 100644 --- a/www/pages/plugins/resource.md +++ b/www/pages/plugins/resource.md @@ -1,5 +1,4 @@ --- -label: 'Resource' menu: side title: 'Resource' index: 4 diff --git a/www/pages/plugins/rollup.md b/www/pages/plugins/rollup.md index cc16e4465..6cfd260a4 100644 --- a/www/pages/plugins/rollup.md +++ b/www/pages/plugins/rollup.md @@ -1,5 +1,4 @@ --- -label: 'Rollup' menu: side title: 'Rollup' index: 6 diff --git a/www/pages/plugins/server.md b/www/pages/plugins/server.md index 80730a885..fa0171739 100644 --- a/www/pages/plugins/server.md +++ b/www/pages/plugins/server.md @@ -1,5 +1,4 @@ --- -label: 'Server' menu: side title: 'Server' index: 7 diff --git a/www/pages/plugins/source.md b/www/pages/plugins/source.md index b5aa03ec5..2f42e31c1 100644 --- a/www/pages/plugins/source.md +++ b/www/pages/plugins/source.md @@ -1,5 +1,4 @@ --- -label: 'source' menu: side title: 'Source' index: 8 From c4a0d4a09db50967aa8e7fe8ee8e76f427271a68 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Sat, 24 Aug 2024 21:39:45 -0400 Subject: [PATCH 05/36] refactor graph title behavior --- packages/cli/src/lib/layout-utils.js | 24 ++++++----- packages/cli/src/lifecycles/bundle.js | 4 +- packages/cli/src/lifecycles/graph.js | 18 ++++----- .../plugins/resource/plugin-standard-html.js | 40 +++++-------------- ...orkspace-layouts-page-bare-merging.spec.js | 2 +- www/pages/about/community.md | 1 - www/pages/about/features.md | 1 - www/pages/about/goals.md | 1 - www/pages/about/how-it-works.md | 1 - www/pages/about/index.md | 1 - www/pages/about/tech-stack.md | 1 - www/pages/blog/index.md | 1 - www/pages/docs/api-routes.md | 1 - www/pages/docs/component-model.md | 1 - www/pages/docs/configuration.md | 1 - www/pages/docs/css-and-images.md | 1 - www/pages/docs/data.md | 1 - www/pages/docs/index.md | 1 - www/pages/docs/layouts.md | 1 - www/pages/docs/markdown.md | 1 - www/pages/docs/menus.md | 1 - www/pages/docs/scripts.md | 1 - www/pages/docs/server-rendering.md | 1 - www/pages/getting-started/branding.md | 1 - www/pages/getting-started/creating-content.md | 1 - www/pages/getting-started/index.md | 1 - www/pages/getting-started/key-concepts.md | 1 - www/pages/getting-started/next-steps.md | 1 - www/pages/getting-started/optimizing.md | 1 - www/pages/getting-started/project-setup.md | 1 - www/pages/getting-started/quick-start.md | 1 - .../guides/cloudflare-workers-deployment.md | 1 - www/pages/guides/index.md | 1 - www/pages/guides/netlify-cms.md | 1 - www/pages/plugins/adapter.md | 1 - www/pages/plugins/context.md | 1 - www/pages/plugins/copy.md | 1 - www/pages/plugins/custom-plugins.md | 1 - www/pages/plugins/index.md | 1 - www/pages/plugins/renderer.md | 1 - www/pages/plugins/resource.md | 1 - www/pages/plugins/rollup.md | 1 - www/pages/plugins/server.md | 1 - www/pages/plugins/source.md | 1 - 44 files changed, 37 insertions(+), 90 deletions(-) diff --git a/packages/cli/src/lib/layout-utils.js b/packages/cli/src/lib/layout-utils.js index d52a6c214..a3cb47738 100644 --- a/packages/cli/src/lib/layout-utils.js +++ b/packages/cli/src/lib/layout-utils.js @@ -108,7 +108,8 @@ async function getPageLayout(filePath, compilation, layout) { } /* eslint-disable-next-line complexity */ -async function getAppLayout(pageLayoutContents, compilation, customImports = [], frontmatterTitle) { +async function getAppLayout(pageLayoutContents, compilation, customImports = [], matchingRoute) { + const activeFrontmatterTitleKey = '${globalThis.page.title}'; const enableHud = compilation.config.devServer.hud; const { layoutsDir, userLayoutsDir } = compilation.context; const userStaticAppLayoutUrl = new URL('./app.html', userLayoutsDir); @@ -193,20 +194,25 @@ async function getAppLayout(pageLayoutContents, compilation, customImports = [], const appBody = appRoot.querySelector('body') ? appRoot.querySelector('body').innerHTML : ''; const pageBody = pageRoot && pageRoot.querySelector('body') ? pageRoot.querySelector('body').innerHTML : ''; const pageTitle = pageRoot && pageRoot.querySelector('head title'); - const hasActiveFrontmatterTitle = pageTitle && pageTitle.rawText.indexOf('${globalThis.page.title}') >= 0 - || appTitle && appTitle.rawText.indexOf('${globalThis.page.title}') >= 0; + const hasActiveFrontmatterTitle = pageTitle && pageTitle.rawText.indexOf(activeFrontmatterTitleKey) >= 0 + || appTitle && appTitle.rawText.indexOf(activeFrontmatterTitleKey) >= 0; + let title; - const title = hasActiveFrontmatterTitle // favor active frontmatter title first - ? pageTitle && pageTitle.rawText + if (hasActiveFrontmatterTitle) { + const text = pageTitle && pageTitle.rawText.indexOf(activeFrontmatterTitleKey) >= 0 ? pageTitle.rawText - : appTitle.rawText - : frontmatterTitle // otherwise, work in order of specificity from page -> page layout -> app layout - ? frontmatterTitle + : appTitle.rawText; + + title = text.replace(activeFrontmatterTitleKey, matchingRoute.title || matchingRoute.label); + } else { + title = matchingRoute.title + ? matchingRoute.title : pageTitle && pageTitle.rawText ? pageTitle.rawText : appTitle && appTitle.rawText ? appTitle.rawText - : 'My App'; + : matchingRoute.label; + } const mergedHtml = pageRoot && pageRoot.querySelector('html').rawAttrs !== '' ? `` diff --git a/packages/cli/src/lifecycles/bundle.js b/packages/cli/src/lifecycles/bundle.js index 9c963f786..d5f1a5037 100644 --- a/packages/cli/src/lifecycles/bundle.js +++ b/packages/cli/src/lifecycles/bundle.js @@ -243,7 +243,7 @@ async function bundleSsrPages(compilation, optimizePlugins) { // and before we optimize so that all bundled assets can tracked up front // would be nice to see if this can be done in a single pass though... for (const page of ssrPages) { - const { imports, route, layout, title, relativeWorkspacePagePath } = page; + const { imports, route, layout, relativeWorkspacePagePath } = page; const moduleUrl = new URL(`.${relativeWorkspacePagePath}`, pagesDir); const request = new Request(moduleUrl); // TODO getLayout has to be static (for now?) @@ -252,7 +252,7 @@ async function bundleSsrPages(compilation, optimizePlugins) { let staticHtml = ''; staticHtml = data.layout ? data.layout : await getPageLayout(staticHtml, compilation, layout); - staticHtml = await getAppLayout(staticHtml, compilation, imports, title); + staticHtml = await getAppLayout(staticHtml, compilation, imports, page); staticHtml = await getUserScripts(staticHtml, compilation); staticHtml = await (await interceptPage(new URL(`http://localhost:8080${route}`), new Request(new URL(`http://localhost:8080${route}`)), getPluginInstances(compilation), staticHtml)).text(); diff --git a/packages/cli/src/lifecycles/graph.js b/packages/cli/src/lifecycles/graph.js index 33fd5898d..d285fe292 100644 --- a/packages/cli/src/lifecycles/graph.js +++ b/packages/cli/src/lifecycles/graph.js @@ -5,7 +5,7 @@ import { checkResourceExists, requestAsObject } from '../lib/resource-utils.js'; import toc from 'markdown-toc'; import { Worker } from 'worker_threads'; -function labelFromRoute(_route) { +function getLabelFromRoute(_route) { let route = _route; if (route === '/index/') { @@ -42,8 +42,8 @@ const generateGraph = async (compilation) => { filename: 'index.html', path: '/', route: `${basePath}/`, - id: 'index', - label: 'Index', + label: 'Home', + title: null, data: {}, imports: [], resources: [], @@ -119,8 +119,8 @@ const generateGraph = async (compilation) => { let route = relativePagePath.replace(extension, ''); let root = filename.split('/')[filename.split('/').length - 1].replace(extension, ''); let layout = extension === '.html' ? null : 'page'; - let label = labelFromRoute(`${route}/`); - let title = null; // TODO use label here + let title = null; + let label = getLabelFromRoute(`${route}/`); let imports = []; let customData = {}; let filePath; @@ -267,14 +267,14 @@ const generateGraph = async (compilation) => { * data: custom page frontmatter * filename: base filename of the page * relativeWorkspacePagePath: the file path relative to the user's workspace directory - * label: "pretty" text representation of the filename + * label: by default is just a copy of title, otherwise can be overridden by the user * imports: per page JS or CSS file imports to be included in HTML output from frontmatter * resources: sum of all resources for the entire page * outputPath: the filename to write to when generating static HTML * path: path to the file relative to the workspace * route: URL route for a given page on outputFilePath * layout: page layout to use as a base for a generated component - * title: a default value that can be used for + * title: A way to customize the tag of the page, otherwise defaults tot the value of label * isSSR: if this is a server side route * prerender: if this should be statically exported * isolation: if this should be run in isolated mode @@ -357,8 +357,8 @@ const generateGraph = async (compilation) => { filename: '404.html', route: `${basePath}/404/`, path: '404.html', - id: '404', - label: 'Not Found' + label: 'Not Found', + title: null } ]; } diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 6650bea29..828e3a94c 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -5,7 +5,6 @@ * This is a Greenwood default plugin. * */ -import frontmatter from 'front-matter'; import fs from 'fs/promises'; import rehypeStringify from 'rehype-stringify'; import rehypeRaw from 'rehype-raw'; @@ -46,9 +45,7 @@ class StandardHtmlResource extends ResourceInterface { const isMarkdownContent = (matchingRoute?.filename || '').split('.').pop() === 'md'; let body = ''; - let title = matchingRoute.title || null; let layout = matchingRoute.layout || null; - let frontMatter = matchingRoute.data || {}; let customImports = matchingRoute.imports || []; let ssrBody; let ssrLayout; @@ -74,7 +71,6 @@ class StandardHtmlResource extends ResourceInterface { } const settings = config.markdown.settings || {}; - const fm = frontmatter(markdownContents); // TODO we already got this once in the graph phase... processedMarkdown = await unified() .use(remarkParse, settings) // parse markdown into AST @@ -85,23 +81,6 @@ class StandardHtmlResource extends ResourceInterface { .use(rehypePlugins) // apply userland rehype plugins .use(rehypeStringify) // convert AST to HTML string .process(markdownContents); - - // configure via frontmatter - if (fm.attributes) { - frontMatter = fm.attributes; - - if (frontMatter.title) { - title = frontMatter.title; - } - - if (frontMatter.layout) { - layout = frontMatter.layout; - } - - if (frontMatter.imports) { - customImports = frontMatter.imports; - } - } } if (matchingRoute.isSSR) { @@ -144,7 +123,7 @@ class StandardHtmlResource extends ResourceInterface { body = ssrLayout ? ssrLayout : await getPageLayout(filePath, this.compilation, layout); } - body = await getAppLayout(body, this.compilation, customImports, title); + body = await getAppLayout(body, this.compilation, customImports, matchingRoute); body = await getUserScripts(body, this.compilation); if (processedMarkdown) { @@ -172,21 +151,22 @@ class StandardHtmlResource extends ResourceInterface { } if (activeFrontmatter) { - console.log({ frontMatter, matchingRoute }); - // TODO consolidate this for (const fm in matchingRoute.data) { - console.log('11', { fm }); const interpolatedFrontmatter = '\\$\\{globalThis.page.' + fm + '\\}'; const needle = typeof matchingRoute.data[fm] === 'string' ? matchingRoute.data[fm] : JSON.stringify(matchingRoute.data[fm]).replace(/"/g, '"'); - console.log('replace', needle); + body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), needle); } - for (const fm in frontMatter) { - const interpolatedFrontmatter = '\\$\\{globalThis.page.' + fm + '\\}'; + // TODO + // const activeFrontmatterForwardKeys = ['route', 'label']; - body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), frontMatter[fm]); - } + // for (const key of activeFrontmatterForwardKeys) { + // console.log({ key }) + // const interpolatedFrontmatter = '\\$\\{globalThis.page.' + key + '\\}'; + + // body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), matchingRoute[key]); + // } for (const collection in this.compilation.collections) { const interpolatedFrontmatter = '\\$\\{globalThis.collection.' + collection + '\\}'; diff --git a/packages/cli/test/cases/build.default.workspace-layouts-page-bare-merging/build.default.workspace-layouts-page-bare-merging.spec.js b/packages/cli/test/cases/build.default.workspace-layouts-page-bare-merging/build.default.workspace-layouts-page-bare-merging.spec.js index efc58645f..79795a65f 100644 --- a/packages/cli/test/cases/build.default.workspace-layouts-page-bare-merging/build.default.workspace-layouts-page-bare-merging.spec.js +++ b/packages/cli/test/cases/build.default.workspace-layouts-page-bare-merging/build.default.workspace-layouts-page-bare-merging.spec.js @@ -29,7 +29,7 @@ import { fileURLToPath, URL } from 'url'; const expect = chai.expect; describe('Build Greenwood With: ', function() { - const LABEL = 'Default Greenwood Configuration and Workspace for Quick Start'; + const LABEL = 'Default Greenwood Configuration w/ Bare Page Merging'; const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); const outputPath = fileURLToPath(new URL('.', import.meta.url)); let runner; diff --git a/www/pages/about/community.md b/www/pages/about/community.md index 829ea6b0e..13bbf254c 100644 --- a/www/pages/about/community.md +++ b/www/pages/about/community.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Community' index: 3 --- diff --git a/www/pages/about/features.md b/www/pages/about/features.md index 4c3f51bbb..4d99a1d2c 100644 --- a/www/pages/about/features.md +++ b/www/pages/about/features.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Features' index: 2 linkheadings: 3 --- diff --git a/www/pages/about/goals.md b/www/pages/about/goals.md index 006e8ff19..1450081a8 100644 --- a/www/pages/about/goals.md +++ b/www/pages/about/goals.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Goals' index: 0 --- diff --git a/www/pages/about/how-it-works.md b/www/pages/about/how-it-works.md index a3404e500..2f424a0ce 100644 --- a/www/pages/about/how-it-works.md +++ b/www/pages/about/how-it-works.md @@ -1,6 +1,5 @@ --- menu: side -title: 'How It Works' index: 1 linkheadings: 3 --- diff --git a/www/pages/about/index.md b/www/pages/about/index.md index 488169132..0a2b53bea 100644 --- a/www/pages/about/index.md +++ b/www/pages/about/index.md @@ -1,6 +1,5 @@ --- menu: navigation -title: About index: 1 --- diff --git a/www/pages/about/tech-stack.md b/www/pages/about/tech-stack.md index 80e23c248..d9157256d 100644 --- a/www/pages/about/tech-stack.md +++ b/www/pages/about/tech-stack.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Tech Stack' index: 4 linkheadings: 3 --- diff --git a/www/pages/blog/index.md b/www/pages/blog/index.md index 42ef6ad83..56beda899 100644 --- a/www/pages/blog/index.md +++ b/www/pages/blog/index.md @@ -1,6 +1,5 @@ --- menu: navigation -title: Blog index: 6 layout: blog --- diff --git a/www/pages/docs/api-routes.md b/www/pages/docs/api-routes.md index 2c3d52bdf..3e7c901da 100644 --- a/www/pages/docs/api-routes.md +++ b/www/pages/docs/api-routes.md @@ -1,5 +1,4 @@ --- -label: 'API Routes' menu: side title: 'API Routes' index: 9 diff --git a/www/pages/docs/component-model.md b/www/pages/docs/component-model.md index f472c3463..29d63b7df 100644 --- a/www/pages/docs/component-model.md +++ b/www/pages/docs/component-model.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Component Model' index: 1 --- diff --git a/www/pages/docs/configuration.md b/www/pages/docs/configuration.md index 966e0a3d8..244077af1 100644 --- a/www/pages/docs/configuration.md +++ b/www/pages/docs/configuration.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Configuration' index: 2 linkheadings: 3 --- diff --git a/www/pages/docs/css-and-images.md b/www/pages/docs/css-and-images.md index 9d7e2afe7..40ee821fc 100644 --- a/www/pages/docs/css-and-images.md +++ b/www/pages/docs/css-and-images.md @@ -1,5 +1,4 @@ --- -label: 'Styles and Assets' menu: side title: 'Styles and Assets' index: 6 diff --git a/www/pages/docs/data.md b/www/pages/docs/data.md index ca8c58477..880b47583 100644 --- a/www/pages/docs/data.md +++ b/www/pages/docs/data.md @@ -1,5 +1,4 @@ --- -label: 'Data Sources' menu: side title: 'Data Sources' index: 11 diff --git a/www/pages/docs/index.md b/www/pages/docs/index.md index 8c1a8d3fc..f60c1c27d 100644 --- a/www/pages/docs/index.md +++ b/www/pages/docs/index.md @@ -1,6 +1,5 @@ --- menu: navigation -title: Docs index: 2 --- diff --git a/www/pages/docs/layouts.md b/www/pages/docs/layouts.md index c7c61335e..f68fff52e 100644 --- a/www/pages/docs/layouts.md +++ b/www/pages/docs/layouts.md @@ -1,5 +1,4 @@ --- -label: 'Layouts and Pages' menu: side title: 'Layouts and Pages' index: 7 diff --git a/www/pages/docs/markdown.md b/www/pages/docs/markdown.md index 85cdc73c4..962642a53 100644 --- a/www/pages/docs/markdown.md +++ b/www/pages/docs/markdown.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Markdown' index: 4 linkheadings: 3 --- diff --git a/www/pages/docs/menus.md b/www/pages/docs/menus.md index 20743d42c..10970b3b4 100644 --- a/www/pages/docs/menus.md +++ b/www/pages/docs/menus.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Menus' index: 10 linkheadings: 3 --- diff --git a/www/pages/docs/scripts.md b/www/pages/docs/scripts.md index 02175be22..554155173 100644 --- a/www/pages/docs/scripts.md +++ b/www/pages/docs/scripts.md @@ -1,5 +1,4 @@ --- -label: 'Scripts and Imports' menu: side title: 'Scripts and Imports' index: 5 diff --git a/www/pages/docs/server-rendering.md b/www/pages/docs/server-rendering.md index e261d27bb..2219094e6 100644 --- a/www/pages/docs/server-rendering.md +++ b/www/pages/docs/server-rendering.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Server Rendering' index: 8 linkheadings: 3 --- diff --git a/www/pages/getting-started/branding.md b/www/pages/getting-started/branding.md index f83dd4e8d..12b6c1b3f 100644 --- a/www/pages/getting-started/branding.md +++ b/www/pages/getting-started/branding.md @@ -1,5 +1,4 @@ --- -label: 'Components and Styles' menu: side title: 'Components and Styles' index: 5 diff --git a/www/pages/getting-started/creating-content.md b/www/pages/getting-started/creating-content.md index 83906c25e..67e77d06d 100644 --- a/www/pages/getting-started/creating-content.md +++ b/www/pages/getting-started/creating-content.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Creating Content' index: 4 linkheadings: 3 --- diff --git a/www/pages/getting-started/index.md b/www/pages/getting-started/index.md index d8f0f5b7a..19e33fd30 100644 --- a/www/pages/getting-started/index.md +++ b/www/pages/getting-started/index.md @@ -1,6 +1,5 @@ --- menu: navigation -title: 'Getting Started' index: 3 --- diff --git a/www/pages/getting-started/key-concepts.md b/www/pages/getting-started/key-concepts.md index 9ebcab817..024434805 100644 --- a/www/pages/getting-started/key-concepts.md +++ b/www/pages/getting-started/key-concepts.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Key Concepts' index: 3 linkheadings: 3 --- diff --git a/www/pages/getting-started/next-steps.md b/www/pages/getting-started/next-steps.md index c1e171cf6..adec4d4c5 100644 --- a/www/pages/getting-started/next-steps.md +++ b/www/pages/getting-started/next-steps.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Next Steps' index: 8 --- diff --git a/www/pages/getting-started/optimizing.md b/www/pages/getting-started/optimizing.md index 86f8b5727..e85a78a58 100644 --- a/www/pages/getting-started/optimizing.md +++ b/www/pages/getting-started/optimizing.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Optimizing' index: 7 linkheadings: 3 --- diff --git a/www/pages/getting-started/project-setup.md b/www/pages/getting-started/project-setup.md index 5d0049b6f..6bbb0e429 100644 --- a/www/pages/getting-started/project-setup.md +++ b/www/pages/getting-started/project-setup.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Project Setup' index: 2 linkheadings: 3 --- diff --git a/www/pages/getting-started/quick-start.md b/www/pages/getting-started/quick-start.md index 48cada4b9..18b5fbd68 100644 --- a/www/pages/getting-started/quick-start.md +++ b/www/pages/getting-started/quick-start.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Quick Start' index: 1 linkheadings: 3 --- diff --git a/www/pages/guides/cloudflare-workers-deployment.md b/www/pages/guides/cloudflare-workers-deployment.md index ca469a6e9..46892bad1 100644 --- a/www/pages/guides/cloudflare-workers-deployment.md +++ b/www/pages/guides/cloudflare-workers-deployment.md @@ -1,5 +1,4 @@ --- -title: 'Cloudflare Workers Deployment' menu: side linkheadings: 3 index: 6 diff --git a/www/pages/guides/index.md b/www/pages/guides/index.md index 9b97f5e54..6452d6dc5 100644 --- a/www/pages/guides/index.md +++ b/www/pages/guides/index.md @@ -1,5 +1,4 @@ --- -title: 'Guides' menu: navigation index: 5 --- diff --git a/www/pages/guides/netlify-cms.md b/www/pages/guides/netlify-cms.md index 490793b02..beaabd2b6 100644 --- a/www/pages/guides/netlify-cms.md +++ b/www/pages/guides/netlify-cms.md @@ -1,5 +1,4 @@ --- -label: 'Netlify CMS' title: 'Netlify CMS' menu: side linkheadings: 3 diff --git a/www/pages/plugins/adapter.md b/www/pages/plugins/adapter.md index 406d7b36e..8d74c3f80 100644 --- a/www/pages/plugins/adapter.md +++ b/www/pages/plugins/adapter.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Adapter' index: 1 --- diff --git a/www/pages/plugins/context.md b/www/pages/plugins/context.md index e464336b2..6ab81c6d1 100644 --- a/www/pages/plugins/context.md +++ b/www/pages/plugins/context.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Context' index: 2 --- diff --git a/www/pages/plugins/copy.md b/www/pages/plugins/copy.md index 46c47ba56..8aeca20db 100644 --- a/www/pages/plugins/copy.md +++ b/www/pages/plugins/copy.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Copy' index: 3 --- diff --git a/www/pages/plugins/custom-plugins.md b/www/pages/plugins/custom-plugins.md index dfe30ee3a..9fd431392 100644 --- a/www/pages/plugins/custom-plugins.md +++ b/www/pages/plugins/custom-plugins.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Custom Plugins' index: 9 --- diff --git a/www/pages/plugins/index.md b/www/pages/plugins/index.md index 58d58e97b..7c0861fa5 100644 --- a/www/pages/plugins/index.md +++ b/www/pages/plugins/index.md @@ -1,6 +1,5 @@ --- menu: navigation -title: Plugins index: 4 --- diff --git a/www/pages/plugins/renderer.md b/www/pages/plugins/renderer.md index bfa734766..8620e4eae 100644 --- a/www/pages/plugins/renderer.md +++ b/www/pages/plugins/renderer.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Renderer' index: 5 --- diff --git a/www/pages/plugins/resource.md b/www/pages/plugins/resource.md index a22cb3462..d81c52949 100644 --- a/www/pages/plugins/resource.md +++ b/www/pages/plugins/resource.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Resource' index: 4 --- diff --git a/www/pages/plugins/rollup.md b/www/pages/plugins/rollup.md index 6cfd260a4..190c65c38 100644 --- a/www/pages/plugins/rollup.md +++ b/www/pages/plugins/rollup.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Rollup' index: 6 --- diff --git a/www/pages/plugins/server.md b/www/pages/plugins/server.md index fa0171739..ac6913925 100644 --- a/www/pages/plugins/server.md +++ b/www/pages/plugins/server.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Server' index: 7 --- diff --git a/www/pages/plugins/source.md b/www/pages/plugins/source.md index 2f42e31c1..e74f7f583 100644 --- a/www/pages/plugins/source.md +++ b/www/pages/plugins/source.md @@ -1,6 +1,5 @@ --- menu: side -title: 'Source' index: 8 --- From bf0d8d15be9ccf5cb7be490ce4d10df2c019dadc Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Thu, 5 Sep 2024 20:27:39 -0400 Subject: [PATCH 06/36] full graph and graphql plugin refactoring --- packages/cli/src/config/rollup.config.js | 4 +- packages/cli/src/lib/layout-utils.js | 13 +- packages/cli/src/lifecycles/bundle.js | 25 +- packages/cli/src/lifecycles/graph.js | 170 +++---- packages/cli/src/lifecycles/serve.js | 6 +- .../src/plugins/resource/plugin-api-routes.js | 2 +- .../plugins/resource/plugin-standard-html.js | 16 +- .../plugins/resource/plugin-static-router.js | 2 +- .../build.default.ssr-static-export.spec.js | 6 +- .../src/pages/artists.js | 10 +- .../build.default.workspace-nested.spec.js | 66 ++- .../build.plugins.source/greenwood.config.js | 6 +- .../cases/develop.ssr/develop.ssr.spec.js | 6 +- .../cases/develop.ssr/src/pages/artists.js | 10 +- .../serve.config.base-path.spec.js | 4 +- .../src/pages/artists.js | 8 +- .../serve.default.ssr.spec.js | 4 +- .../serve.default.ssr/src/pages/artists.js | 10 +- packages/plugin-graphql/README.md | 2 +- packages/plugin-graphql/src/index.js | 3 +- .../plugin-graphql/src/queries/children.gql | 10 +- .../plugin-graphql/src/queries/collection.gql | 14 + .../plugin-graphql/src/queries/config.gql | 11 - packages/plugin-graphql/src/queries/graph.gql | 10 +- packages/plugin-graphql/src/queries/menu.gql | 20 - packages/plugin-graphql/src/schema/config.js | 35 -- packages/plugin-graphql/src/schema/graph.js | 98 ++-- packages/plugin-graphql/src/schema/schema.js | 3 - .../develop.default/develop.default.spec.js | 3 +- .../loaders-prerender.query-children.spec.js | 2 +- .../qraphql-server/graphql-server.spec.js | 4 +- .../query-children/query-children.spec.js | 2 +- .../greenwood.config.js | 0 .../package.json | 2 +- .../query-collection.spec.js} | 8 +- .../src/components/header.js | 8 +- .../src/layouts/page.html | 0 .../src/pages/about.md | 3 +- .../src/pages/contact.md | 3 +- .../src/pages/index.md | 0 .../cases/query-config/query-config.spec.js | 116 ----- .../query-config/src/components/footer.js | 22 - .../cases/query-config/src/pages/index.html | 12 - .../cases/query-graph/query-graph.spec.js | 2 +- .../cases/query-graph/src/pages/index.html | 2 +- .../test/cases/query-menu/greenwood.config.js | 11 - .../plugin-graphql/test/unit/common.spec.js | 8 +- .../plugin-graphql/test/unit/mocks/config.js | 12 - .../plugin-graphql/test/unit/mocks/graph.js | 272 +++++------ .../test/unit/schema/config.spec.js | 53 -- .../test/unit/schema/graph.children.spec.js | 85 ++++ .../test/unit/schema/graph.collection.spec.js | 65 +++ .../test/unit/schema/graph.menu.spec.js | 456 ------------------ .../test/unit/schema/graph.spec.js | 31 +- .../cases/serve.default/serve.default.spec.js | 14 +- .../cases/serve.default/src/pages/artists.js | 10 +- 56 files changed, 551 insertions(+), 1229 deletions(-) create mode 100644 packages/plugin-graphql/src/queries/collection.gql delete mode 100644 packages/plugin-graphql/src/queries/config.gql delete mode 100644 packages/plugin-graphql/src/queries/menu.gql delete mode 100644 packages/plugin-graphql/src/schema/config.js rename packages/plugin-graphql/test/cases/{query-config => query-collection}/greenwood.config.js (100%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/package.json (57%) rename packages/plugin-graphql/test/cases/{query-menu/query-menu.spec.js => query-collection/query-collection.spec.js} (96%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/src/components/header.js (83%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/src/layouts/page.html (100%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/src/pages/about.md (61%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/src/pages/contact.md (65%) rename packages/plugin-graphql/test/cases/{query-menu => query-collection}/src/pages/index.md (100%) delete mode 100644 packages/plugin-graphql/test/cases/query-config/query-config.spec.js delete mode 100644 packages/plugin-graphql/test/cases/query-config/src/components/footer.js delete mode 100644 packages/plugin-graphql/test/cases/query-config/src/pages/index.html delete mode 100644 packages/plugin-graphql/test/cases/query-menu/greenwood.config.js delete mode 100644 packages/plugin-graphql/test/unit/mocks/config.js delete mode 100644 packages/plugin-graphql/test/unit/schema/config.spec.js create mode 100644 packages/plugin-graphql/test/unit/schema/graph.children.spec.js create mode 100644 packages/plugin-graphql/test/unit/schema/graph.collection.spec.js delete mode 100644 packages/plugin-graphql/test/unit/schema/graph.menu.spec.js diff --git a/packages/cli/src/config/rollup.config.js b/packages/cli/src/config/rollup.config.js index 53734d8e7..fa565a2e9 100644 --- a/packages/cli/src/config/rollup.config.js +++ b/packages/cli/src/config/rollup.config.js @@ -354,7 +354,7 @@ function greenwoodImportMetaUrl(compilation) { for (const entry of compilation.manifest.apis.keys()) { const apiRoute = compilation.manifest.apis.get(entry); - if (normalizedId.endsWith(apiRoute.path)) { + if (normalizedId.endsWith(apiRoute.pagePath.replace('.', ''))) { const assets = apiRoute.assets || []; assets.push(assetUrl.url.href); @@ -646,7 +646,7 @@ const getRollupConfigForApiRoutes = async (compilation) => { const { outputDir, pagesDir, apisDir } = compilation.context; return [...compilation.manifest.apis.values()] - .map(api => normalizePathnameForWindows(new URL(`.${api.path}`, pagesDir))) + .map(api => normalizePathnameForWindows(new URL(api.pagePath, pagesDir))) .map((filepath) => { // account for windows pathname shenanigans by "casting" filepath to a URL first const ext = filepath.split('.').pop(); diff --git a/packages/cli/src/lib/layout-utils.js b/packages/cli/src/lib/layout-utils.js index a3cb47738..abc63888c 100644 --- a/packages/cli/src/lib/layout-utils.js +++ b/packages/cli/src/lib/layout-utils.js @@ -30,10 +30,10 @@ async function getCustomPageLayoutsFromPlugins(compilation, layoutName) { return customLayoutLocations; } -async function getPageLayout(filePath, compilation, layout) { +async function getPageLayout(filePath = '', compilation, layout) { const { config, context } = compilation; - const { layoutsDir, userLayoutsDir, pagesDir, projectDirectory } = context; - const filePathUrl = new URL(`${filePath}`, projectDirectory); + const { layoutsDir, userLayoutsDir, pagesDir } = context; + const filePathUrl = new URL(filePath, pagesDir); const customPageFormatPlugins = config.plugins .filter(plugin => plugin.type === 'resource' && !plugin.isGreenwoodDefaultPlugin) .map(plugin => plugin.provider(compilation)); @@ -43,13 +43,13 @@ async function getPageLayout(filePath, compilation, layout) { && await customPageFormatPlugins[0].shouldServe(filePathUrl); const customPluginDefaultPageLayouts = await getCustomPageLayoutsFromPlugins(compilation, 'page'); const customPluginPageLayouts = await getCustomPageLayoutsFromPlugins(compilation, layout); - const extension = filePath.split('.').pop(); - const is404Page = filePath.startsWith('404') && extension === 'html'; + const extension = filePath?.split('.')?.pop(); + const is404Page = filePath?.endsWith('404.html') && extension === 'html'; const hasCustomStaticLayout = await checkResourceExists(new URL(`./${layout}.html`, userLayoutsDir)); const hasCustomDynamicLayout = await checkResourceExists(new URL(`./${layout}.js`, userLayoutsDir)); const hasPageLayout = await checkResourceExists(new URL('./page.html', userLayoutsDir)); const hasCustom404Page = await checkResourceExists(new URL('./404.html', pagesDir)); - const isHtmlPage = extension === 'html' && await checkResourceExists(new URL(`./${filePath}`, projectDirectory)); + const isHtmlPage = extension === 'html' && await checkResourceExists(new URL(filePath, pagesDir)); let contents; if (layout && (customPluginPageLayouts.length > 0 || hasCustomStaticLayout)) { @@ -113,7 +113,6 @@ async function getAppLayout(pageLayoutContents, compilation, customImports = [], const enableHud = compilation.config.devServer.hud; const { layoutsDir, userLayoutsDir } = compilation.context; const userStaticAppLayoutUrl = new URL('./app.html', userLayoutsDir); - // TODO support more than just .js files const userDynamicAppLayoutUrl = new URL('./app.js', userLayoutsDir); const userHasStaticAppLayout = await checkResourceExists(userStaticAppLayoutUrl); const userHasDynamicAppLayout = await checkResourceExists(userDynamicAppLayoutUrl); diff --git a/packages/cli/src/lifecycles/bundle.js b/packages/cli/src/lifecycles/bundle.js index d5f1a5037..ae467bddc 100644 --- a/packages/cli/src/lifecycles/bundle.js +++ b/packages/cli/src/lifecycles/bundle.js @@ -243,15 +243,13 @@ async function bundleSsrPages(compilation, optimizePlugins) { // and before we optimize so that all bundled assets can tracked up front // would be nice to see if this can be done in a single pass though... for (const page of ssrPages) { - const { imports, route, layout, relativeWorkspacePagePath } = page; - const moduleUrl = new URL(`.${relativeWorkspacePagePath}`, pagesDir); + const { imports, route, layout, pagePath } = page; + const moduleUrl = new URL(pagePath, pagesDir); const request = new Request(moduleUrl); - // TODO getLayout has to be static (for now?) - // https://github.com/ProjectEvergreen/greenwood/issues/955 const data = await executeRouteModule({ moduleUrl, compilation, page, prerender: false, htmlContents: null, scripts: [], request }); let staticHtml = ''; - staticHtml = data.layout ? data.layout : await getPageLayout(staticHtml, compilation, layout); + staticHtml = data.layout ? data.layout : await getPageLayout(pagePath, compilation, layout); staticHtml = await getAppLayout(staticHtml, compilation, imports, page); staticHtml = await getUserScripts(staticHtml, compilation); staticHtml = await (await interceptPage(new URL(`http://localhost:8080${route}`), new Request(new URL(`http://localhost:8080${route}`)), getPluginInstances(compilation), staticHtml)).text(); @@ -268,14 +266,13 @@ async function bundleSsrPages(compilation, optimizePlugins) { // second pass to link all bundled assets to their resources before optimizing and generating SSR bundles for (const page of ssrPages) { - const { filename, route, relativeWorkspacePagePath } = page; - const entryFileUrl = new URL(`.${relativeWorkspacePagePath}`, scratchDir); - const outputPathRootUrl = new URL(`file://${path.dirname(entryFileUrl.pathname)}`); + const { route, pagePath } = page; + const entryFileUrl = new URL(pagePath, pagesDir); + const entryFileOutputUrl = new URL(`file://${entryFileUrl.pathname.replace(pagesDir.pathname, scratchDir.pathname)}`); + const outputPathRootUrl = new URL(`file://${path.dirname(entryFileOutputUrl.pathname)}/`); const htmlOptimizer = config.plugins.find(plugin => plugin.name === 'plugin-standard-html').provider(compilation); const pagesPathDiff = context.pagesDir.pathname.replace(context.projectDirectory.pathname, ''); - const relativeDepth = relativeWorkspacePagePath.replace(`/${filename}`, '') === '' - ? '../' - : '../'.repeat(relativeWorkspacePagePath.replace(`/${filename}`, '').split('/').length); + const relativeDepth = '../'.repeat(pagePath.split('/').length - 1); let staticHtml = ssrPrerenderPagesRouteMapper[route]; staticHtml = await (await htmlOptimizer.optimize(new URL(`http://localhost:8080${route}`), new Response(staticHtml))).text(); @@ -288,10 +285,10 @@ async function bundleSsrPages(compilation, optimizePlugins) { } // better way to write out this inline code? - await fs.writeFile(entryFileUrl, ` + await fs.writeFile(entryFileOutputUrl, ` import { executeRouteModule } from '${normalizePathnameForWindows(executeModuleUrl)}'; - const moduleUrl = new URL('${relativeDepth}${pagesPathDiff}${relativeWorkspacePagePath.replace('/', '')}', import.meta.url); + const moduleUrl = new URL('${relativeDepth}${pagesPathDiff}${pagePath.replace('./', '')}', import.meta.url); export async function handler(request) { const compilation = JSON.parse('${JSON.stringify(compilation)}'); @@ -311,7 +308,7 @@ async function bundleSsrPages(compilation, optimizePlugins) { } `); - input.push(normalizePathnameForWindows(entryFileUrl)); + input.push(normalizePathnameForWindows(entryFileOutputUrl)); } const ssrConfigs = await getRollupConfigForSsrPages(compilation, input); diff --git a/packages/cli/src/lifecycles/graph.js b/packages/cli/src/lifecycles/graph.js index d285fe292..6523b189b 100644 --- a/packages/cli/src/lifecycles/graph.js +++ b/packages/cli/src/lifecycles/graph.js @@ -30,7 +30,7 @@ const generateGraph = async (compilation) => { try { const { context, config } = compilation; const { basePath } = config; - const { pagesDir, projectDirectory, userWorkspace } = context; + const { pagesDir, userWorkspace } = context; const collections = {}; const customPageFormatPlugins = config.plugins .filter(plugin => plugin.type === 'resource' && !plugin.isGreenwoodDefaultPlugin) @@ -39,8 +39,7 @@ const generateGraph = async (compilation) => { let apis = new Map(); let graph = [{ outputPath: '/index.html', - filename: 'index.html', - path: '/', + pagePath: './src/index.html', route: `${basePath}/`, label: 'Home', title: null, @@ -66,9 +65,8 @@ const generateGraph = async (compilation) => { apiRoutes = nextPages.apiRoutes; } else { const extension = `.${filenameUrl.pathname.split('.').pop()}`; - const relativePagePath = filenameUrl.pathname.replace(pagesDir.pathname, '/'); - const relativeWorkspacePath = directory.pathname.replace(projectDirectory.pathname, ''); - const isApiRoute = relativePagePath.startsWith('/api'); + const relativePagePath = filenameUrl.pathname.replace(pagesDir.pathname, './'); + const isApiRoute = relativePagePath.startsWith('./api'); const req = isApiRoute ? new Request(filenameUrl) : new Request(filenameUrl, { headers: { 'Accept': 'text/html' } }); @@ -84,6 +82,8 @@ const generateGraph = async (compilation) => { const isStatic = isCustom === 'static' || extension === '.md' || extension === '.html'; const isDynamic = isCustom === 'dynamic' || extension === '.js'; const isPage = isStatic || isDynamic; + let route = `${relativePagePath.replace('.', '').replace(`${extension}`, '')}`; + let fileContents; if (isApiRoute) { const extension = filenameUrl.pathname.split('.').pop(); @@ -93,9 +93,7 @@ const generateGraph = async (compilation) => { return; } - const relativeApiPath = filenameUrl.pathname.replace(pagesDir.pathname, '/'); - const route = `${basePath}${relativeApiPath.replace(`.${extension}`, '')}`; - // TODO should this be run in isolation like SSR pages? + // should this be run in isolation like SSR pages? // https://github.com/ProjectEvergreen/greenwood/issues/991 const { isolation } = await import(filenameUrl).then(module => module); @@ -108,22 +106,19 @@ const generateGraph = async (compilation) => { * route: URL route for a given page on outputFilePath * isolation: if this should be run in isolated mode */ - apiRoutes.set(route, { - filename: filename, - outputPath: relativeApiPath, - path: relativeApiPath, - route, + apiRoutes.set(`${basePath}${route}`, { + pagePath: relativePagePath, + outputPath: relativePagePath, + route: `${basePath}${route}`, isolation }); } else if (isPage) { - let route = relativePagePath.replace(extension, ''); let root = filename.split('/')[filename.split('/').length - 1].replace(extension, ''); let layout = extension === '.html' ? null : 'page'; let title = null; let label = getLabelFromRoute(`${route}/`); let imports = []; let customData = {}; - let filePath; let prerender = true; let isolation = false; let hydration = false; @@ -136,7 +131,7 @@ const generateGraph = async (compilation) => { * - pages/blog/index.{html,md,js} -> /blog/ * - pages/blog/some-post.{html,md,js} -> /blog/some-post/ */ - if (relativePagePath.lastIndexOf('/') > 0) { + if (relativePagePath.lastIndexOf('/index') > 0) { // https://github.com/ProjectEvergreen/greenwood/issues/455 route = root === 'index' || route.replace('/index', '') === `/${root}` ? route.replace('index', '') @@ -148,60 +143,19 @@ const generateGraph = async (compilation) => { } if (isStatic) { - const fileContents = await fs.readFile(filenameUrl, 'utf8'); + fileContents = await fs.readFile(filenameUrl, 'utf8'); const { attributes } = fm(fileContents); layout = attributes.layout || layout; title = attributes.title || title; label = attributes.label || label; imports = attributes.imports || []; - filePath = `${relativeWorkspacePath}${filename}`; - // prune "reserved" attributes that are supported by Greenwood - // https://www.greenwoodjs.io/docs/front-matter customData = attributes; - - delete customData.label; - delete customData.imports; - delete customData.title; - delete customData.layout; - - /* Menu Query - * Custom front matter - Variable Definitions - * -------------------------------------------------- - * menu: the name of the menu in which this item can be listed and queried - * index: the index of this list item within a menu - * linkheadings: flag to tell us where to add page's table of contents as menu items - * tableOfContents: json object containing page's table of contents(list of headings) - */ - // set specific menu to place this page - customData.menu = customData.menu || ''; - - // set specific index list priority of this item within a menu - customData.index = customData.index || ''; - - // set flag whether to gather a list of headings on a page as menu items - customData.linkheadings = customData.linkheadings || 0; - customData.tableOfContents = []; - - if (customData.linkheadings > 0) { - // parse markdown for table of contents and output to json - customData.tableOfContents = toc(fileContents).json; - customData.tableOfContents.shift(); - - // parse table of contents for only the pages user wants linked - if (customData.tableOfContents.length > 0 && customData.linkheadings > 0) { - customData.tableOfContents = customData.tableOfContents - .filter((item) => item.lvl === customData.linkheadings); - } - } - /* ---------End Menu Query-------------------- */ } else if (isDynamic) { const routeWorkerUrl = compilation.config.plugins.filter(plugin => plugin.type === 'renderer')[0].provider(compilation).executeModuleUrl; let ssrFrontmatter; - filePath = route; - await new Promise(async (resolve, reject) => { const worker = new Worker(new URL('../lib/ssr-route-worker.js', import.meta.url)); const request = await requestAsObject(new Request(filenameUrl)); @@ -245,56 +199,72 @@ const generateGraph = async (compilation) => { layout = ssrFrontmatter.layout || layout; title = ssrFrontmatter.title || title; imports = ssrFrontmatter.imports || imports; - customData = ssrFrontmatter.data || customData; label = ssrFrontmatter.label || label; + customData = ssrFrontmatter || customData; + } + } - /* Menu Query - * Custom front matter - Variable Definitions - * -------------------------------------------------- - * menu: the name of the menu in which this item can be listed and queried - * index: the index of this list item within a menu - * linkheadings: flag to tell us where to add page's table of contents as menu items - * tableOfContents: json object containing page's table of contents(list of headings) - */ - customData.menu = ssrFrontmatter.menu || ''; - customData.index = ssrFrontmatter.index || ''; + /* + * Custom front matter - Variable Definitions + * -------------------------------------------------- + * collection: the name of the collection for the page + * order: the order of this item within the collection + * tocHeading: heading size to use a Table of Contents for a page + * tableOfContents: json object containing page's table of contents (list of headings) + */ + + // prune "reserved" attributes that are supported by Greenwood + // https://www.greenwoodjs.io/docs/front-matter + delete customData.label; + delete customData.imports; + delete customData.title; + delete customData.layout; + + // set flag whether to gather a list of headings on a page as menu items + customData.tocHeading = customData.tocHeading || 0; + customData.tableOfContents = []; + + if (fileContents && customData.tocHeading > 0 && customData.tocHeading <= 6) { + // parse markdown for table of contents and output to json + customData.tableOfContents = toc(fileContents).json; + customData.tableOfContents.shift(); + + // parse table of contents for only the pages user wants linked + if (customData.tableOfContents.length > 0 && customData.tocHeading > 0) { + customData.tableOfContents = customData.tableOfContents + .filter((item) => item.lvl === customData.tocHeading); } } /* - * Graph Properties (per page) - *---------------------- - * data: custom page frontmatter - * filename: base filename of the page - * relativeWorkspacePagePath: the file path relative to the user's workspace directory - * label: by default is just a copy of title, otherwise can be overridden by the user - * imports: per page JS or CSS file imports to be included in HTML output from frontmatter - * resources: sum of all resources for the entire page - * outputPath: the filename to write to when generating static HTML - * path: path to the file relative to the workspace - * route: URL route for a given page on outputFilePath - * layout: page layout to use as a base for a generated component - * title: A way to customize the tag of the page, otherwise defaults tot the value of label - * isSSR: if this is a server side route - * prerender: if this should be statically exported - * isolation: if this should be run in isolated mode - * hydration: if this page needs hydration support - * servePage: signal that this is a custom page file type (static | dynamic) - */ + * Page Properties + *---------------------- + * label: Display text for the page inferred, by default is the value of title + * title: used to customize the tag of the page, inferred from the filename + * route: URL for accessing the page from the browser + * layout: the custom layout of the page + * data: custom page frontmatter + * imports: per page JS or CSS file imports specified from frontmatter + * resources: all script, style, etc resources for the entire page as URLs + * outputPath: the name of the file in the output folder + * isSSR: if this is a server side route + * prerender: if this page should be statically exported + * isolation: if this page should be run in isolated mode + * hydration: if this page needs hydration support + * servePage: signal that this is a custom page file type (static | dynamic) + */ const page = { - data: customData || {}, - filename, - relativeWorkspacePagePath: relativePagePath, label, + title, + route: `${basePath}${route}`, + layout, + data: customData || {}, imports, resources: [], + pagePath: relativePagePath, outputPath: route === '/404/' ? '/404.html' : `${route}index.html`, - path: filePath, - route: `${basePath}${route}`, - layout, - title, isSSR: !isStatic, prerender, isolation, @@ -304,6 +274,7 @@ const generateGraph = async (compilation) => { pages.push(page); + // handle collections const pageCollection = customData.collection; if (pageCollection) { @@ -354,11 +325,11 @@ const generateGraph = async (compilation) => { { ...oldGraph, outputPath: '/404.html', - filename: '404.html', + pagePath: './src/404.html', route: `${basePath}/404/`, path: '404.html', label: 'Not Found', - title: null + title: 'Page Not Found' } ]; } @@ -380,8 +351,7 @@ const generateGraph = async (compilation) => { } graph.push({ - filename: null, - path: null, + pagePath: null, data: {}, imports: [], resources: [], diff --git a/packages/cli/src/lifecycles/serve.js b/packages/cli/src/lifecycles/serve.js index ae31a62e0..f0b95a6c1 100644 --- a/packages/cli/src/lifecycles/serve.js +++ b/packages/cli/src/lifecycles/serve.js @@ -337,7 +337,7 @@ async function getHybridServer(compilation) { if (matchingRoute.isolation || isolationMode) { await new Promise(async (resolve, reject) => { const worker = new Worker(new URL('../lib/ssr-route-worker-isolation-mode.js', import.meta.url)); - // TODO "faux" new Request here, a better way? + // "faux" new Request here, a better way? const request = await requestAsObject(new Request(url)); worker.on('message', async (result) => { @@ -370,13 +370,13 @@ async function getHybridServer(compilation) { ctx.status = 200; } else if (isApiRoute) { const apiRoute = manifest.apis.get(url.pathname); - const entryPointUrl = new URL(`.${apiRoute.outputPath}`, outputDir); + const entryPointUrl = new URL(`./${apiRoute.outputPath}`, outputDir); let body, status, headers, statusText; if (apiRoute.isolation || isolationMode) { await new Promise(async (resolve, reject) => { const worker = new Worker(new URL('../lib/api-route-worker.js', import.meta.url)); - // TODO "faux" new Request here, a better way? + // "faux" new Request here, a better way? const req = await requestAsObject(request); worker.on('message', async (result) => { diff --git a/packages/cli/src/plugins/resource/plugin-api-routes.js b/packages/cli/src/plugins/resource/plugin-api-routes.js index 20de63b35..6e98d05e0 100644 --- a/packages/cli/src/plugins/resource/plugin-api-routes.js +++ b/packages/cli/src/plugins/resource/plugin-api-routes.js @@ -20,7 +20,7 @@ class ApiRoutesResource extends ResourceInterface { async serve(url, request) { const api = this.compilation.manifest.apis.get(url.pathname); - const apiUrl = new URL(`.${api.path}`, this.compilation.context.pagesDir); + const apiUrl = new URL(api.pagePath, this.compilation.context.pagesDir); const href = apiUrl.href; if (process.env.__GWD_COMMAND__ === 'develop') { // eslint-disable-line no-underscore-dangle diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js index 828e3a94c..ab63448d2 100644 --- a/packages/cli/src/plugins/resource/plugin-standard-html.js +++ b/packages/cli/src/plugins/resource/plugin-standard-html.js @@ -36,14 +36,14 @@ class StandardHtmlResource extends ResourceInterface { async serve(url, request) { const { config, context } = this.compilation; - const { pagesDir, userWorkspace } = context; + const { projectDirectory, pagesDir } = context; const { activeFrontmatter } = config; const { pathname } = url; const isSpaRoute = this.compilation.graph.find(node => node.isSPA); const matchingRoute = this.compilation.graph.find((node) => node.route === pathname) || {}; - const filePath = !matchingRoute.external ? matchingRoute.path : ''; - const isMarkdownContent = (matchingRoute?.filename || '').split('.').pop() === 'md'; - + const { pagePath } = matchingRoute; + const filePath = !matchingRoute.external ? pagePath : ''; + const isMarkdownContent = (filePath || '').split('.').pop() === 'md'; let body = ''; let layout = matchingRoute.layout || null; let customImports = matchingRoute.imports || []; @@ -56,7 +56,7 @@ class StandardHtmlResource extends ResourceInterface { } if (isMarkdownContent) { - const markdownContents = await fs.readFile(filePath, 'utf-8'); + const markdownContents = await fs.readFile(new URL(pagePath, pagesDir), 'utf-8'); const rehypePlugins = []; const remarkPlugins = []; @@ -84,7 +84,7 @@ class StandardHtmlResource extends ResourceInterface { } if (matchingRoute.isSSR) { - const routeModuleLocationUrl = new URL(`.${matchingRoute.relativeWorkspacePagePath}`, pagesDir); + const routeModuleLocationUrl = new URL(pagePath, pagesDir); const routeWorkerUrl = this.compilation.config.plugins.find(plugin => plugin.type === 'renderer').provider().executeModuleUrl; await new Promise(async (resolve, reject) => { @@ -118,9 +118,9 @@ class StandardHtmlResource extends ResourceInterface { } if (isSpaRoute) { - body = await fs.readFile(new URL(`./${isSpaRoute.filename}`, userWorkspace), 'utf-8'); + body = await fs.readFile(new URL(isSpaRoute.pagePath, projectDirectory), 'utf-8'); } else { - body = ssrLayout ? ssrLayout : await getPageLayout(filePath, this.compilation, layout); + body = ssrLayout ? ssrLayout : await getPageLayout(pagePath, this.compilation, layout); } body = await getAppLayout(body, this.compilation, customImports, matchingRoute); diff --git a/packages/cli/src/plugins/resource/plugin-static-router.js b/packages/cli/src/plugins/resource/plugin-static-router.js index 4145ec595..f039e6145 100644 --- a/packages/cli/src/plugins/resource/plugin-static-router.js +++ b/packages/cli/src/plugins/resource/plugin-static-router.js @@ -69,7 +69,7 @@ class StaticRouterResource extends ResourceInterface { .filter(page => !page.isSSR) .filter(page => !page.route.endsWith('/404/')) .map((page) => { - const layout = page.filename && page.filename.split('.').pop() === this.extensions[0] + const layout = page.pagePath && page.pagePath.split('.').pop() === this.extensions[0] ? page.route : page.layout; const key = page.route === '/' diff --git a/packages/cli/test/cases/build.default.ssr-static-export/build.default.ssr-static-export.spec.js b/packages/cli/test/cases/build.default.ssr-static-export/build.default.ssr-static-export.spec.js index 762f5fe1d..a7f484293 100644 --- a/packages/cli/test/cases/build.default.ssr-static-export/build.default.ssr-static-export.spec.js +++ b/packages/cli/test/cases/build.default.ssr-static-export/build.default.ssr-static-export.spec.js @@ -195,9 +195,9 @@ describe('Build Greenwood With: ', function() { expect(artistsPageGraphData).to.not.be.undefined; }); - it('should have the expected menu and index values in the graph', function() { - expect(artistsPageGraphData.data.menu).to.equal('navigation'); - expect(artistsPageGraphData.data.index).to.equal(7); + it('should have the expected collection and order values in the graph', function() { + expect(artistsPageGraphData.data.collection).to.equal('navigation'); + expect(artistsPageGraphData.data.order).to.equal(7); }); it('should have expected custom data values in its graph data', function() { diff --git a/packages/cli/test/cases/build.default.ssr-static-export/src/pages/artists.js b/packages/cli/test/cases/build.default.ssr-static-export/src/pages/artists.js index 8a80b59ad..5ef61a70b 100644 --- a/packages/cli/test/cases/build.default.ssr-static-export/src/pages/artists.js +++ b/packages/cli/test/cases/build.default.ssr-static-export/src/pages/artists.js @@ -70,15 +70,13 @@ async function getBody(compilation) { async function getFrontmatter() { return { - menu: 'navigation', - index: 7, + collection: 'navigation', + order: 7, imports: [ '/components/counter.js' ], - data: { - author: 'Project Evergreen', - date: '01-01-2021' - } + author: 'Project Evergreen', + date: '01-01-2021' }; } diff --git a/packages/cli/test/cases/build.default.workspace-nested/build.default.workspace-nested.spec.js b/packages/cli/test/cases/build.default.workspace-nested/build.default.workspace-nested.spec.js index f14766089..78ec14761 100644 --- a/packages/cli/test/cases/build.default.workspace-nested/build.default.workspace-nested.spec.js +++ b/packages/cli/test/cases/build.default.workspace-nested/build.default.workspace-nested.spec.js @@ -76,38 +76,32 @@ describe('Build Greenwood With: ', function() { let graph; before(async function() { - graph = JSON.parse(await fs.promises.readFile(path.join(this.context.publicDir, 'graph.json'), 'utf-8')) - .map(item => { - return { - ...item, - path: item.path.replace(/\\/g, '/') - }; - }); + graph = JSON.parse(await fs.promises.readFile(path.join(this.context.publicDir, 'graph.json'), 'utf-8')); }); it('should have the expected ordering of pages in graph.json', function() { expect(graph.length).to.equal(21); - expect(graph[0].path).to.be.equal('src/pages/blog/2017/03/26/index.md'); - expect(graph[1].path).to.be.equal('src/pages/blog/2017/03/30/index.md'); - expect(graph[2].path).to.be.equal('src/pages/blog/2017/04/10/index.md'); - expect(graph[3].path).to.be.equal('src/pages/blog/2017/04/22/index.md'); - expect(graph[4].path).to.be.equal('src/pages/blog/2017/05/05/index.md'); - expect(graph[5].path).to.be.equal('src/pages/blog/2017/06/07/index.md'); - expect(graph[6].path).to.be.equal('src/pages/blog/2017/09/10/index.md'); - expect(graph[7].path).to.be.equal('src/pages/blog/2017/10/15/index.md'); - expect(graph[8].path).to.be.equal('src/pages/blog/2018/01/24/index.md'); - expect(graph[9].path).to.be.equal('src/pages/blog/2018/05/16/index.md'); - expect(graph[10].path).to.be.equal('src/pages/blog/2018/06/06/index.md'); - expect(graph[11].path).to.be.equal('src/pages/blog/2018/09/26/index.md'); - expect(graph[12].path).to.be.equal('src/pages/blog/2018/10/28/index.md'); - expect(graph[13].path).to.be.equal('src/pages/blog/2018/11/19/index.md'); - expect(graph[14].path).to.be.equal('src/pages/blog/2019/11/11/index.md'); - expect(graph[15].path).to.be.equal('src/pages/blog/2020/04/07/index.md'); - expect(graph[16].path).to.be.equal('src/pages/blog/2020/08/15/index.md'); - expect(graph[17].path).to.be.equal('src/pages/blog/2020/10/28/index.md'); - expect(graph[18].path).to.be.equal('src/pages/blog/index.md'); - expect(graph[19].path).to.be.equal('src/pages/index.html'); - expect(graph[20].path).to.be.equal('404.html'); + expect(graph[0].pagePath).to.be.equal('./blog/2017/03/26/index.md'); + expect(graph[1].pagePath).to.be.equal('./blog/2017/03/30/index.md'); + expect(graph[2].pagePath).to.be.equal('./blog/2017/04/10/index.md'); + expect(graph[3].pagePath).to.be.equal('./blog/2017/04/22/index.md'); + expect(graph[4].pagePath).to.be.equal('./blog/2017/05/05/index.md'); + expect(graph[5].pagePath).to.be.equal('./blog/2017/06/07/index.md'); + expect(graph[6].pagePath).to.be.equal('./blog/2017/09/10/index.md'); + expect(graph[7].pagePath).to.be.equal('./blog/2017/10/15/index.md'); + expect(graph[8].pagePath).to.be.equal('./blog/2018/01/24/index.md'); + expect(graph[9].pagePath).to.be.equal('./blog/2018/05/16/index.md'); + expect(graph[10].pagePath).to.be.equal('./blog/2018/06/06/index.md'); + expect(graph[11].pagePath).to.be.equal('./blog/2018/09/26/index.md'); + expect(graph[12].pagePath).to.be.equal('./blog/2018/10/28/index.md'); + expect(graph[13].pagePath).to.be.equal('./blog/2018/11/19/index.md'); + expect(graph[14].pagePath).to.be.equal('./blog/2019/11/11/index.md'); + expect(graph[15].pagePath).to.be.equal('./blog/2020/04/07/index.md'); + expect(graph[16].pagePath).to.be.equal('./blog/2020/08/15/index.md'); + expect(graph[17].pagePath).to.be.equal('./blog/2020/10/28/index.md'); + expect(graph[18].pagePath).to.be.equal('./blog/index.md'); + expect(graph[19].pagePath).to.be.equal('./index.html'); + expect(graph[20].pagePath).to.be.equal('./src/404.html'); }); it('should create a top level blog pages directory', function() { @@ -125,8 +119,8 @@ describe('Build Greenwood With: ', function() { graph.filter((page) => { return page.route.indexOf('2017') > 0; }).forEach((page) => { - const outputpath = path.join(this.context.publicDir, page.route, 'index.html'); - expect(fs.existsSync(outputpath)).to.be.true; + const outputPath = path.join(this.context.publicDir, page.route, 'index.html'); + expect(fs.existsSync(outputPath)).to.be.true; }); }); @@ -134,8 +128,8 @@ describe('Build Greenwood With: ', function() { graph.filter((page) => { return page.route.indexOf('2018') > 0; }).forEach((page) => { - const outputpath = path.join(this.context.publicDir, page.route, 'index.html'); - expect(fs.existsSync(outputpath)).to.be.true; + const outputPath = path.join(this.context.publicDir, page.route, 'index.html'); + expect(fs.existsSync(outputPath)).to.be.true; }); }); @@ -143,8 +137,8 @@ describe('Build Greenwood With: ', function() { graph.filter((page) => { return page.route.indexOf('2019') > 0; }).forEach((page) => { - const outputpath = path.join(this.context.publicDir, page.route, 'index.html'); - expect(fs.existsSync(outputpath)).to.be.true; + const outputPath = path.join(this.context.publicDir, page.route, 'index.html'); + expect(fs.existsSync(outputPath)).to.be.true; }); }); @@ -152,8 +146,8 @@ describe('Build Greenwood With: ', function() { graph.filter((page) => { return page.route.indexOf('2020') > 0; }).forEach((page) => { - const outputpath = path.join(this.context.publicDir, page.route, 'index.html'); - expect(fs.existsSync(outputpath)).to.be.true; + const outputPath = path.join(this.context.publicDir, page.route, 'index.html'); + expect(fs.existsSync(outputPath)).to.be.true; }); }); diff --git a/packages/cli/test/cases/build.plugins.source/greenwood.config.js b/packages/cli/test/cases/build.plugins.source/greenwood.config.js index 6487b68f7..6990ca243 100644 --- a/packages/cli/test/cases/build.plugins.source/greenwood.config.js +++ b/packages/cli/test/cases/build.plugins.source/greenwood.config.js @@ -18,12 +18,10 @@ const customExternalSourcesPlugin = { `, route, - id, layout: 'artist', label: name, - data: { - imageUrl - } + imageUrl, + id }; }); }; diff --git a/packages/cli/test/cases/develop.ssr/develop.ssr.spec.js b/packages/cli/test/cases/develop.ssr/develop.ssr.spec.js index 6ae34e4ff..04deff113 100644 --- a/packages/cli/test/cases/develop.ssr/develop.ssr.spec.js +++ b/packages/cli/test/cases/develop.ssr/develop.ssr.spec.js @@ -214,9 +214,9 @@ describe('Develop Greenwood With: ', function() { expect(artistsPageGraphData).to.not.be.undefined; }); - it('should have the expected menu and index values in the graph', function() { - expect(artistsPageGraphData.data.menu).to.equal('navigation'); - expect(artistsPageGraphData.data.index).to.equal(7); + it('should have the expected collection and order values in the graph', function() { + expect(artistsPageGraphData.data.collection).to.equal('navigation'); + expect(artistsPageGraphData.data.order).to.equal(7); }); it('should have expected custom data values in its graph data', function() { diff --git a/packages/cli/test/cases/develop.ssr/src/pages/artists.js b/packages/cli/test/cases/develop.ssr/src/pages/artists.js index 5c7a58388..6fe976afd 100644 --- a/packages/cli/test/cases/develop.ssr/src/pages/artists.js +++ b/packages/cli/test/cases/develop.ssr/src/pages/artists.js @@ -69,16 +69,14 @@ async function getBody(compilation) { async function getFrontmatter(compilation, { route }) { return { - menu: 'navigation', - index: 7, + collection: 'navigation', + order: 7, title: route, imports: [ '/components/counter.js' ], - data: { - author: 'Project Evergreen', - date: '01-01-2021' - } + author: 'Project Evergreen', + date: '01-01-2021' }; } diff --git a/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js b/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js index 5782be9fd..110598c0c 100644 --- a/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js +++ b/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js @@ -356,7 +356,7 @@ describe('Serve Greenwood With: ', function() { }); - describe('Develop command with dev proxy', function() { + describe('Serve command with dev proxy', function() { let response = {}; let data; @@ -381,7 +381,7 @@ describe('Serve Greenwood With: ', function() { }); }); - describe('Develop command with API specific behaviors', function() { + describe('Serve command with API specific behaviors', function() { const name = 'Greenwood'; let response = {}; let data = {}; diff --git a/packages/cli/test/cases/serve.default.ssr-static-export/src/pages/artists.js b/packages/cli/test/cases/serve.default.ssr-static-export/src/pages/artists.js index 8a80b59ad..fbf4c75f4 100644 --- a/packages/cli/test/cases/serve.default.ssr-static-export/src/pages/artists.js +++ b/packages/cli/test/cases/serve.default.ssr-static-export/src/pages/artists.js @@ -70,15 +70,9 @@ async function getBody(compilation) { async function getFrontmatter() { return { - menu: 'navigation', - index: 7, imports: [ '/components/counter.js' - ], - data: { - author: 'Project Evergreen', - date: '01-01-2021' - } + ] }; } diff --git a/packages/cli/test/cases/serve.default.ssr/serve.default.ssr.spec.js b/packages/cli/test/cases/serve.default.ssr/serve.default.ssr.spec.js index c19c4c814..35c819b42 100644 --- a/packages/cli/test/cases/serve.default.ssr/serve.default.ssr.spec.js +++ b/packages/cli/test/cases/serve.default.ssr/serve.default.ssr.spec.js @@ -198,8 +198,8 @@ describe('Serve Greenwood With: ', function() { it('should have the expected menu and index values in the graph', function() { expect(artistsPageGraphData.label).to.equal('Artists'); - expect(artistsPageGraphData.data.menu).to.equal('navigation'); - expect(artistsPageGraphData.data.index).to.equal(7); + expect(artistsPageGraphData.data.collection).to.equal('navigation'); + expect(artistsPageGraphData.data.order).to.equal(7); }); it('should have expected custom data values in its graph data', function() { diff --git a/packages/cli/test/cases/serve.default.ssr/src/pages/artists.js b/packages/cli/test/cases/serve.default.ssr/src/pages/artists.js index d930eeb69..fc77f2725 100644 --- a/packages/cli/test/cases/serve.default.ssr/src/pages/artists.js +++ b/packages/cli/test/cases/serve.default.ssr/src/pages/artists.js @@ -70,16 +70,14 @@ async function getBody(compilation) { async function getFrontmatter(compilation, { route }) { return { - menu: 'navigation', + collection: 'navigation', title: route, - index: 7, + order: 7, imports: [ '/components/counter.js' ], - data: { - author: 'Project Evergreen', - date: '01-01-2021' - } + author: 'Project Evergreen', + date: '01-01-2021' }; } diff --git a/packages/plugin-graphql/README.md b/packages/plugin-graphql/README.md index 290e682a9..2e1738d5f 100644 --- a/packages/plugin-graphql/README.md +++ b/packages/plugin-graphql/README.md @@ -59,7 +59,7 @@ class HeaderComponent extends HTMLElement { query: MenuQuery, variables: { name: 'navigation', - order: 'index_asc' + order: 'order_asc' } }); diff --git a/packages/plugin-graphql/src/index.js b/packages/plugin-graphql/src/index.js index 05ac42787..3642c4096 100644 --- a/packages/plugin-graphql/src/index.js +++ b/packages/plugin-graphql/src/index.js @@ -9,9 +9,8 @@ const importMap = { '@greenwood/plugin-graphql/src/core/client.js': '/node_modules/@greenwood/plugin-graphql/src/core/client.js', '@greenwood/plugin-graphql/src/core/common.js': '/node_modules/@greenwood/plugin-graphql/src/core/common.js', '@greenwood/plugin-graphql/src/queries/children.gql': '/node_modules/@greenwood/plugin-graphql/src/queries/children.gql', - '@greenwood/plugin-graphql/src/queries/config.gql': '/node_modules/@greenwood/plugin-graphql/src/queries/config.gql', '@greenwood/plugin-graphql/src/queries/graph.gql': '/node_modules/@greenwood/plugin-graphql/src/queries/graph.gql', - '@greenwood/plugin-graphql/src/queries/menu.gql': '/node_modules/@greenwood/plugin-graphql/src/queries/menu.gql' + '@greenwood/plugin-graphql/src/queries/collection.gql': '/node_modules/@greenwood/plugin-graphql/src/queries/collection.gql' }; class GraphQLResource extends ResourceInterface { diff --git a/packages/plugin-graphql/src/queries/children.gql b/packages/plugin-graphql/src/queries/children.gql index e544a0eaa..c337c5515 100644 --- a/packages/plugin-graphql/src/queries/children.gql +++ b/packages/plugin-graphql/src/queries/children.gql @@ -1,12 +1,14 @@ query($parent: String!) { children(parent: $parent) { - id, - filename, label, + title, + workspacePath, outputPath, - path, route, layout, - title + tableOfContents { + label, + route + } } } \ No newline at end of file diff --git a/packages/plugin-graphql/src/queries/collection.gql b/packages/plugin-graphql/src/queries/collection.gql new file mode 100644 index 000000000..e7aabaa79 --- /dev/null +++ b/packages/plugin-graphql/src/queries/collection.gql @@ -0,0 +1,14 @@ +query($name: String!, $orderBy: CollectionOrderBy) { + collection(name: $name, orderBy: $orderBy) { + label, + title, + workspacePath, + outputPath, + route, + layout, + tableOfContents { + label, + route + } + } +} \ No newline at end of file diff --git a/packages/plugin-graphql/src/queries/config.gql b/packages/plugin-graphql/src/queries/config.gql deleted file mode 100644 index 797d13ac4..000000000 --- a/packages/plugin-graphql/src/queries/config.gql +++ /dev/null @@ -1,11 +0,0 @@ -query { - config { - devServer { - port - }, - staticRouter, - optimization, - prerender, - workspace - } -} \ No newline at end of file diff --git a/packages/plugin-graphql/src/queries/graph.gql b/packages/plugin-graphql/src/queries/graph.gql index 0b85879e7..2ca92d8b5 100644 --- a/packages/plugin-graphql/src/queries/graph.gql +++ b/packages/plugin-graphql/src/queries/graph.gql @@ -1,12 +1,14 @@ query { graph { - id, - filename, label, + title, + workspacePath, outputPath, - path, route, layout, - title + tableOfContents { + label, + route + } } } \ No newline at end of file diff --git a/packages/plugin-graphql/src/queries/menu.gql b/packages/plugin-graphql/src/queries/menu.gql deleted file mode 100644 index ad205cec3..000000000 --- a/packages/plugin-graphql/src/queries/menu.gql +++ /dev/null @@ -1,20 +0,0 @@ -query($name: String, $route: String, $order: MenuOrderBy) { - menu(name: $name, pathname: $route, orderBy: $order) { - item { - label, - route - } - children { - item { - label, - route - }, - children { - item { - label, - route - } - } - } - } -} \ No newline at end of file diff --git a/packages/plugin-graphql/src/schema/config.js b/packages/plugin-graphql/src/schema/config.js deleted file mode 100644 index d7400fdc1..000000000 --- a/packages/plugin-graphql/src/schema/config.js +++ /dev/null @@ -1,35 +0,0 @@ -import gql from 'graphql-tag'; - -const getConfiguration = async (root, query, context) => { - return context.config; -}; - -// https://www.greenwoodjs.io/docs/configuration -const configTypeDefs = gql` - type DevServer { - port: Int - } - - type Config { - devServer: DevServer, - staticRouter: Boolean, - optimization: String, - prerender: Boolean, - workspace: String - } - - extend type Query { - config: Config - } -`; - -const configResolvers = { - Query: { - config: getConfiguration - } -}; - -export { - configTypeDefs, - configResolvers -}; \ No newline at end of file diff --git a/packages/plugin-graphql/src/schema/graph.js b/packages/plugin-graphql/src/schema/graph.js index 8fbce7cab..472019e52 100644 --- a/packages/plugin-graphql/src/schema/graph.js +++ b/packages/plugin-graphql/src/schema/graph.js @@ -1,54 +1,43 @@ import gql from 'graphql-tag'; -const getMenuFromGraph = async (root, { name, pathname, orderBy }, context) => { +const getCollection = (root, { name, orderBy }, context) => { const { graph } = context; - const { basePath } = context.config; let items = []; graph .forEach((page) => { - const { route, data, label } = page; - const { menu, index, tableOfContents, linkheadings } = data; - let children = getParsedHeadingsFromPage(tableOfContents, linkheadings); - - if (menu && menu.search(name) > -1) { - if (pathname) { - const normalizedRoute = basePath === '' - ? route - : route.replace(basePath, ''); - - if (normalizedRoute.startsWith(pathname)) { - items.push({ item: { route, label, index }, children }); - } - } else { - items.push({ item: { route, label, index }, children }); - } + const { data, label, title } = page; + const { collection, tableOfContents, tocHeading } = data; + const toc = getParsedHeadingsFromPage(tableOfContents, tocHeading); + + if (collection === name) { + items.push({ ...page, title: title || label, tableOfContents: toc }); } }); - if (orderBy !== '') { - items = sortMenuItems(items, orderBy); + if (orderBy) { + items = sortCollection(items, orderBy); } - return Promise.resolve({ item: { label: name }, children: items }); + return items; }; -const sortMenuItems = (menuItems, order) => { +const sortCollection = (collection, orderBy) => { const compare = (a, b) => { - if (order === 'title_asc' || order === 'title_desc') { - a = a.item.label, b = b.item.label; + if (orderBy === 'title_asc' || orderBy === 'title_desc') { + a = a?.title, b = b?.title; } - if (order === 'index_asc' || order === 'index_desc') { - a = a.item.index, b = b.item.index; + if (orderBy === 'order_asc' || orderBy === 'order_desc') { + a = a?.data.order, b = b?.data?.order; } - if (order === 'title_asc' || order === 'index_asc') { + if (orderBy === 'title_asc' || orderBy === 'order_asc') { if (a < b) { return -1; } if (a > b) { return 1; } - } else if (order === 'title_desc' || order === 'index_desc') { + } else if (orderBy === 'title_desc' || orderBy === 'order_desc') { if (a > b) { return -1; } @@ -59,8 +48,7 @@ const sortMenuItems = (menuItems, order) => { return 0; }; - menuItems.sort(compare); - return menuItems; + return collection.sort(compare); }; const getParsedHeadingsFromPage = (tableOfContents = [], headingLevel) => { @@ -70,10 +58,11 @@ const getParsedHeadingsFromPage = (tableOfContents = [], headingLevel) => { tableOfContents.forEach(({ content, slug, lvl }) => { // make sure we only add heading elements of the same level (h1, h2, h3) if (lvl === headingLevel) { - children.push({ item: { label: content, route: '#' + slug }, children: [] }); + children.push({ label: content, route: '#' + slug }); } }); } + return children; }; @@ -85,17 +74,14 @@ const getChildrenFromParentRoute = async (root, query, context) => { const pages = []; const { parent } = query; const { graph } = context; - const { basePath } = context.config; + // TODO handle base path + // const { basePath } = context.config; graph .forEach((page) => { - const { route, path } = page; - const normalizedRoute = basePath === '' - ? route - : route.replace(basePath, '/'); - const root = normalizedRoute.split('/')[1]; + const { route } = page; - if (`/${root}` === parent && path.indexOf(`${parent}/index.md`) < 0) { + if (`${parent}/` !== route && route.startsWith(parent)) { pages.push(page); } }); @@ -104,46 +90,40 @@ const getChildrenFromParentRoute = async (root, query, context) => { }; const graphTypeDefs = gql` - type Page { - data: Data, - filename: String, - id: String, + type TocItem { label: String, - path: String, - outputPath: String, route: String, - layout: String, - title: String } - type Link { + type Page { label: String, - route: String - } - - type Menu { - item: Link - children: [Menu] + title: String, + route: String, + layout: String, + data: Data, + outputPath: String, + workspacePath: String, + tableOfContents: [TocItem] } - enum MenuOrderBy { + enum CollectionOrderBy { title_asc, title_desc - index_asc, - index_desc + order_asc, + order_desc } type Query { graph: [Page] - menu(name: String, orderBy: MenuOrderBy, pathname: String): Menu - children(parent: String): [Page] + collection(name: String!, orderBy: CollectionOrderBy): [Page] + children(parent: String!): [Page] } `; const graphResolvers = { Query: { graph: getPagesFromGraph, - menu: getMenuFromGraph, + collection: getCollection, children: getChildrenFromParentRoute } }; diff --git a/packages/plugin-graphql/src/schema/schema.js b/packages/plugin-graphql/src/schema/schema.js index 346f89d82..d029eb860 100644 --- a/packages/plugin-graphql/src/schema/schema.js +++ b/packages/plugin-graphql/src/schema/schema.js @@ -1,6 +1,5 @@ import { checkResourceExists } from '@greenwood/cli/src/lib/resource-utils.js'; import { makeExecutableSchema } from 'apollo-server-express'; -import { configTypeDefs, configResolvers } from './config.js'; import { graphTypeDefs, graphResolvers } from './graph.js'; import fs from 'fs/promises'; import gql from 'graphql-tag'; @@ -51,7 +50,6 @@ const createSchema = async (compilation) => { const mergedResolvers = Object.assign({}, { Query: { ...graphResolvers.Query, - ...configResolvers.Query, ...customUserResolvers.reduce((resolvers, resolver) => { return { ...resolvers, @@ -64,7 +62,6 @@ const createSchema = async (compilation) => { const schema = makeExecutableSchema({ typeDefs: [ graphTypeDefs, - configTypeDefs, customDataDefs, ...customUserDefs ], diff --git a/packages/plugin-graphql/test/cases/develop.default/develop.default.spec.js b/packages/plugin-graphql/test/cases/develop.default/develop.default.spec.js index 07a6d4f2b..8924c8c20 100644 --- a/packages/plugin-graphql/test/cases/develop.default/develop.default.spec.js +++ b/packages/plugin-graphql/test/cases/develop.default/develop.default.spec.js @@ -83,9 +83,8 @@ describe('Develop Greenwood With: ', function() { expect(importMap['@greenwood/plugin-graphql/src/core/client.js']).to.equal('/node_modules/@greenwood/plugin-graphql/src/core/client.js'); expect(importMap['@greenwood/plugin-graphql/src/core/common.js']).to.equal('/node_modules/@greenwood/plugin-graphql/src/core/common.js'); expect(importMap['@greenwood/plugin-graphql/src/queries/children.gql']).to.equal('/node_modules/@greenwood/plugin-graphql/src/queries/children.gql'); - expect(importMap['@greenwood/plugin-graphql/src/queries/config.gql']).to.equal('/node_modules/@greenwood/plugin-graphql/src/queries/config.gql'); expect(importMap['@greenwood/plugin-graphql/src/queries/graph.gql']).to.equal('/node_modules/@greenwood/plugin-graphql/src/queries/graph.gql'); - expect(importMap['@greenwood/plugin-graphql/src/queries/menu.gql']).to.equal('/node_modules/@greenwood/plugin-graphql/src/queries/menu.gql'); + expect(importMap['@greenwood/plugin-graphql/src/queries/collection.gql']).to.equal('/node_modules/@greenwood/plugin-graphql/src/queries/collection.gql'); done(); }); diff --git a/packages/plugin-graphql/test/cases/loaders-prerender.query-children/loaders-prerender.query-children.spec.js b/packages/plugin-graphql/test/cases/loaders-prerender.query-children/loaders-prerender.query-children.spec.js index a30c06160..947e63a4b 100644 --- a/packages/plugin-graphql/test/cases/loaders-prerender.query-children/loaders-prerender.query-children.spec.js +++ b/packages/plugin-graphql/test/cases/loaders-prerender.query-children/loaders-prerender.query-children.spec.js @@ -125,7 +125,7 @@ describe('Build Greenwood With: ', function() { expect(lists.length).to.be.equal(1); }); - it('should have a expected navigation output in the
based on pages with menu: navigation frontmatter', function() { + it('should have a expected navigation output in the
based on pages that are children of the blog route', function() { const listItems = dom.window.document.querySelectorAll('body ul li'); expect(listItems.length).to.be.equal(2); diff --git a/packages/plugin-graphql/test/cases/qraphql-server/graphql-server.spec.js b/packages/plugin-graphql/test/cases/qraphql-server/graphql-server.spec.js index 91dda8bde..5c13aea98 100644 --- a/packages/plugin-graphql/test/cases/qraphql-server/graphql-server.spec.js +++ b/packages/plugin-graphql/test/cases/qraphql-server/graphql-server.spec.js @@ -82,7 +82,7 @@ describe('Develop Greenwood With: ', function() { const body = { 'operationName': null, 'variables': {}, - 'query': '{\n config {\n workspace\n }\n}\n' + 'query': '{\n graph {\n label\n }\n}\n' }; before(async function() { @@ -105,7 +105,7 @@ describe('Develop Greenwood With: ', function() { }); it('should return the expected query response', function() { - expect(data.data.config.workspace).to.equal(new URL('./src/', import.meta.url).href); + expect(data.data.graph).to.not.be.undefined; }); }); }); diff --git a/packages/plugin-graphql/test/cases/query-children/query-children.spec.js b/packages/plugin-graphql/test/cases/query-children/query-children.spec.js index 20c5793f8..bb0b8da92 100644 --- a/packages/plugin-graphql/test/cases/query-children/query-children.spec.js +++ b/packages/plugin-graphql/test/cases/query-children/query-children.spec.js @@ -187,7 +187,7 @@ describe('Build Greenwood With: ', function() { expect(lists.length).to.be.equal(1); }); - it('should have a expected navigation output in the
based on pages with menu: navigation frontmatter', function() { + it('should have a expected navigation output in the
based on pages that are children of the blog route', function() { const listItems = dom.window.document.querySelectorAll('body ul li'); expect(listItems.length).to.be.equal(2); diff --git a/packages/plugin-graphql/test/cases/query-config/greenwood.config.js b/packages/plugin-graphql/test/cases/query-collection/greenwood.config.js similarity index 100% rename from packages/plugin-graphql/test/cases/query-config/greenwood.config.js rename to packages/plugin-graphql/test/cases/query-collection/greenwood.config.js diff --git a/packages/plugin-graphql/test/cases/query-menu/package.json b/packages/plugin-graphql/test/cases/query-collection/package.json similarity index 57% rename from packages/plugin-graphql/test/cases/query-menu/package.json rename to packages/plugin-graphql/test/cases/query-collection/package.json index 3d07f5b69..db5e66341 100644 --- a/packages/plugin-graphql/test/cases/query-menu/package.json +++ b/packages/plugin-graphql/test/cases/query-collection/package.json @@ -1,5 +1,5 @@ { - "name": "plugin-graphql-test-menu-query", + "name": "plugin-graphql-test-collection-query", "type": "module", "dependencies": { "lit": "^3.1.0" diff --git a/packages/plugin-graphql/test/cases/query-menu/query-menu.spec.js b/packages/plugin-graphql/test/cases/query-collection/query-collection.spec.js similarity index 96% rename from packages/plugin-graphql/test/cases/query-menu/query-menu.spec.js rename to packages/plugin-graphql/test/cases/query-collection/query-collection.spec.js index b6f5eb1af..9e61cc965 100644 --- a/packages/plugin-graphql/test/cases/query-menu/query-menu.spec.js +++ b/packages/plugin-graphql/test/cases/query-collection/query-collection.spec.js @@ -11,7 +11,7 @@ * User Command * greenwood build * - * Default Config (+ plugin-graphql and prerender) + * Default Config (+ plugin-graphql) * * Custom Workspace * src/ @@ -37,7 +37,7 @@ import { fileURLToPath, URL } from 'url'; const expect = chai.expect; describe('Build Greenwood With: ', async function() { - const LABEL = 'MenuQuery from GraphQL'; + const LABEL = 'CollectionQuery from GraphQL'; const apolloStateRegex = /window.__APOLLO_STATE__ = true/; const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); const outputPath = fileURLToPath(new URL('.', import.meta.url)); @@ -149,7 +149,7 @@ describe('Build Greenwood With: ', async function() { runSmokeTest(['public', 'index'], LABEL); - describe('Home Page navigation w/ MenuQuery', function() { + describe('Home Page navigation w/ CollectionQuery for navigation', function() { let dom; before(async function() { @@ -187,7 +187,7 @@ describe('Build Greenwood With: ', async function() { expect(headers.length).to.be.equal(1); }); - it('should have a expected navigation output in the
based on pages with menu: navigation frontmatter', function() { + it('should have a expected navigation output in the
based on pages with collection: navigation frontmatter', function() { const listItems = dom.window.document.querySelectorAll('body header ul li'); const link1 = listItems[0].querySelector('a'); const link2 = listItems[1].querySelector('a'); diff --git a/packages/plugin-graphql/test/cases/query-menu/src/components/header.js b/packages/plugin-graphql/test/cases/query-collection/src/components/header.js similarity index 83% rename from packages/plugin-graphql/test/cases/query-menu/src/components/header.js rename to packages/plugin-graphql/test/cases/query-collection/src/components/header.js index a28380f3f..408b75a21 100644 --- a/packages/plugin-graphql/test/cases/query-menu/src/components/header.js +++ b/packages/plugin-graphql/test/cases/query-collection/src/components/header.js @@ -1,6 +1,6 @@ import { LitElement, html } from 'lit'; import client from '@greenwood/plugin-graphql/src/core/client.js'; -import MenuQuery from '@greenwood/plugin-graphql/src/queries/menu.gql'; +import CollectionQuery from '@greenwood/plugin-graphql/src/queries/collection.gql'; class HeaderComponent extends LitElement { @@ -21,13 +21,13 @@ class HeaderComponent extends LitElement { super.connectedCallback(); const response = await client.query({ - query: MenuQuery, + query: CollectionQuery, variables: { - menu: 'navigation' + name: 'navigation' } }); - this.navigation = response.data.menu.children.map(item => item.item); + this.navigation = response.data.collection; } /* eslint-disable indent */ diff --git a/packages/plugin-graphql/test/cases/query-menu/src/layouts/page.html b/packages/plugin-graphql/test/cases/query-collection/src/layouts/page.html similarity index 100% rename from packages/plugin-graphql/test/cases/query-menu/src/layouts/page.html rename to packages/plugin-graphql/test/cases/query-collection/src/layouts/page.html diff --git a/packages/plugin-graphql/test/cases/query-menu/src/pages/about.md b/packages/plugin-graphql/test/cases/query-collection/src/pages/about.md similarity index 61% rename from packages/plugin-graphql/test/cases/query-menu/src/pages/about.md rename to packages/plugin-graphql/test/cases/query-collection/src/pages/about.md index 116be1c43..b1be36c3b 100644 --- a/packages/plugin-graphql/test/cases/query-menu/src/pages/about.md +++ b/packages/plugin-graphql/test/cases/query-collection/src/pages/about.md @@ -1,5 +1,6 @@ --- -menu: navigation +collection: navigation +order: 1 --- ## About Page diff --git a/packages/plugin-graphql/test/cases/query-menu/src/pages/contact.md b/packages/plugin-graphql/test/cases/query-collection/src/pages/contact.md similarity index 65% rename from packages/plugin-graphql/test/cases/query-menu/src/pages/contact.md rename to packages/plugin-graphql/test/cases/query-collection/src/pages/contact.md index 1c18cef3c..014d133bf 100644 --- a/packages/plugin-graphql/test/cases/query-menu/src/pages/contact.md +++ b/packages/plugin-graphql/test/cases/query-collection/src/pages/contact.md @@ -1,5 +1,6 @@ --- -menu: navigation +collection: navigation +order: 2 --- ## Contact Page diff --git a/packages/plugin-graphql/test/cases/query-menu/src/pages/index.md b/packages/plugin-graphql/test/cases/query-collection/src/pages/index.md similarity index 100% rename from packages/plugin-graphql/test/cases/query-menu/src/pages/index.md rename to packages/plugin-graphql/test/cases/query-collection/src/pages/index.md diff --git a/packages/plugin-graphql/test/cases/query-config/query-config.spec.js b/packages/plugin-graphql/test/cases/query-config/query-config.spec.js deleted file mode 100644 index a49fb5c64..000000000 --- a/packages/plugin-graphql/test/cases/query-config/query-config.spec.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Use Case - * Run Greenwood build command with GraphQL calls to get data from the project configuration. - * - * Needs prerender to be true to get SSR and client side GQL fetching. - * - * User Result - * Should generate a Greenwood build that dynamically serializes data from the config in the footer. - * - * User Command - * greenwood build - * - * Default Config (+ plugin-graphql and prerender) - * - * Custom Workspace - * greenwood.config.js - * src/ - * components/ - * footer.js - * pages/ - * index.html - */ -import chai from 'chai'; -import fs from 'fs'; -import glob from 'glob-promise'; -import { JSDOM } from 'jsdom'; -import path from 'path'; -import { runSmokeTest } from '../../../../../test/smoke-test.js'; -import { getSetupFiles, getDependencyFiles, getOutputTeardownFiles } from '../../../../../test/utils.js'; -import { Runner } from 'gallinago'; -import { fileURLToPath, URL } from 'url'; - -const expect = chai.expect; - -describe('Build Greenwood With: ', function() { - const LABEL = 'ConfigQuery from GraphQL'; - const apolloStateRegex = /window.__APOLLO_STATE__ = true/; - const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); - const outputPath = fileURLToPath(new URL('.', import.meta.url)); - let runner; - - before(function() { - this.context = { - publicDir: path.join(outputPath, 'public') - }; - runner = new Runner(); - }); - - describe(LABEL, function() { - - before(async function() { - const greenwoodGraphqlCoreLibs = await getDependencyFiles( - `${process.cwd()}/packages/plugin-graphql/src/core/*.js`, - `${outputPath}/node_modules/@greenwood/plugin-graphql/src/core/` - ); - const greenwoodGraphqlQueryLibs = await getDependencyFiles( - `${process.cwd()}/packages/plugin-graphql/src/queries/*.gql`, - `${outputPath}/node_modules/@greenwood/plugin-graphql/src/queries/` - ); - - runner.setup(outputPath, [ - ...getSetupFiles(outputPath), - ...greenwoodGraphqlCoreLibs, - ...greenwoodGraphqlQueryLibs - ]); - runner.runCommand(cliPath, 'build'); - }); - - runSmokeTest(['public', 'index'], LABEL); - - describe('displaying config optimization in the footer using ConfigQuery', function() { - let dom; - - before(async function() { - dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, 'index.html')); - }); - - it('should have a