', `
+
+
+ `);
+ }
}
export {
diff --git a/packages/cli/src/lifecycles/bundle.js b/packages/cli/src/lifecycles/bundle.js
index 9c963f786..814d1b946 100644
--- a/packages/cli/src/lifecycles/bundle.js
+++ b/packages/cli/src/lifecycles/bundle.js
@@ -83,10 +83,10 @@ async function optimizeStaticPages(compilation, plugins) {
return Promise.all(compilation.graph
.filter(page => !page.isSSR || (page.isSSR && page.prerender) || (page.isSSR && compilation.config.prerender))
.map(async (page) => {
- const { route, outputPath } = page;
- const outputDirUrl = new URL(`.${outputPath.replace('index.html', '').replace('404.html', '')}`, outputDir);
+ const { route, outputHref } = page;
+ const outputDirUrl = new URL(outputHref.replace('index.html', '').replace('404.html', ''));
const url = new URL(`http://localhost:${compilation.config.port}${route}`);
- const contents = await fs.readFile(new URL(`./${outputPath}`, scratchDir), 'utf-8');
+ const contents = await fs.readFile(new URL(`./${outputHref.replace(outputDir.href, '')}`, scratchDir), 'utf-8');
const headers = new Headers({ 'Content-Type': 'text/html' });
let response = new Response(contents, { headers });
@@ -107,7 +107,7 @@ async function optimizeStaticPages(compilation, plugins) {
// clean up optimization markers
const body = (await response.text()).replace(/data-gwd-opt=".*?[a-z]"/g, '');
- await fs.writeFile(new URL(`.${outputPath}`, outputDir), body);
+ await fs.writeFile(new URL(outputHref), body);
})
);
}
@@ -243,16 +243,14 @@ 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 moduleUrl = new URL(`.${relativeWorkspacePagePath}`, pagesDir);
+ const { imports, route, layout, pageHref } = page;
+ const moduleUrl = new URL(pageHref);
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 = await getAppLayout(staticHtml, compilation, imports, title);
+ staticHtml = data.layout ? data.layout : await getPageLayout(pageHref, 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,14 @@ 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 { id, route, pageHref } = page;
+ const pagePath = new URL(pageHref).pathname.replace(pagesDir.pathname, './');
+ const entryFileUrl = new URL(pageHref);
+ 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 +286,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 +309,10 @@ async function bundleSsrPages(compilation, optimizePlugins) {
}
`);
- input.push(normalizePathnameForWindows(entryFileUrl));
+ input.push({
+ id,
+ inputPath: normalizePathnameForWindows(entryFileOutputUrl)
+ });
}
const ssrConfigs = await getRollupConfigForSsrPages(compilation, input);
diff --git a/packages/cli/src/lifecycles/config.js b/packages/cli/src/lifecycles/config.js
index 9c31a230a..37b0621a8 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,
+ activeContent: false,
plugins: greenwoodPlugins,
markdown: { plugins: [], settings: {} },
prerender: false,
@@ -82,7 +82,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, activeContent, isolation, polyfills } = userCfgFile;
// workspace validation
if (workspace) {
@@ -103,11 +103,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 (activeContent) {
+ if (typeof activeContent !== 'boolean') {
+ reject('Error: greenwood.config.js activeContent must be a boolean');
}
- customConfig.interpolateFrontmatter = interpolateFrontmatter;
+ customConfig.activeContent = activeContent;
}
if (plugins && plugins.length > 0) {
diff --git a/packages/cli/src/lifecycles/graph.js b/packages/cli/src/lifecycles/graph.js
index 08055f0ae..3441b55ab 100644
--- a/packages/cli/src/lifecycles/graph.js
+++ b/packages/cli/src/lifecycles/graph.js
@@ -2,28 +2,53 @@
import fs from 'fs/promises';
import fm from 'front-matter';
import { checkResourceExists, requestAsObject } from '../lib/resource-utils.js';
+import { activeFrontmatterKeys } from '../lib/content-utils.js';
import toc from 'markdown-toc';
import { Worker } from 'worker_threads';
+function getLabelFromRoute(_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(' ');
+}
+
+function getIdFromRelativePathPath(relativePathPath, extension) {
+ return relativePathPath.replace(extension, '').replace('./', '').replace(/\//g, '-');
+}
+
const generateGraph = async (compilation) => {
return new Promise(async (resolve, reject) => {
try {
const { context, config } = compilation;
const { basePath } = config;
- const { pagesDir, projectDirectory, userWorkspace } = context;
+ const { pagesDir, userWorkspace, outputDir } = context;
+ const collections = {};
const customPageFormatPlugins = config.plugins
.filter(plugin => plugin.type === 'resource' && !plugin.isGreenwoodDefaultPlugin)
.map(plugin => plugin.provider(compilation));
let apis = new Map();
let graph = [{
- outputPath: '/index.html',
- filename: 'index.html',
- path: '/',
- route: `${basePath}/`,
id: 'index',
- label: 'Index',
+ outputHref: new URL('./index.html', outputDir).href,
+ route: `${basePath}/`,
+ label: 'Home',
+ title: null,
data: {},
imports: [],
resources: [],
@@ -32,7 +57,7 @@ const generateGraph = async (compilation) => {
}];
const walkDirectoryForPages = async function(directory, pages = [], apiRoutes = new Map()) {
- const files = await fs.readdir(directory);
+ const files = (await fs.readdir(directory)).filter(file => !file.startsWith('.'));
for (const filename of files) {
const filenameUrl = new URL(`./${filename}`, directory);
@@ -46,9 +71,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' } });
@@ -64,6 +88,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();
@@ -73,36 +99,34 @@ 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);
/*
* API Properties (per route)
*----------------------
- * filename: base filename of the page
- * outputPath: the filename to write to when generating a build
- * path: path to the file relative to the workspace
+ * id: unique hyphen delimited string of the filename, relative to the page/api directory
+ * pageHref: href to the page's filesystem file
+ * outputHref: href of the filename to write to when generating a build
* 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}`, {
+ id: getIdFromRelativePathPath(relativePagePath, `.${extension}`).replace('api-', ''),
+ pageHref: new URL(relativePagePath, pagesDir).href,
+ outputHref: new URL(relativePagePath, outputDir).href,
+ // outputPath: relativePagePath,
+ route: `${basePath}${route}`,
isolation
});
} 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 = getLabelFromRoute(`${route}/`);
let imports = [];
let customData = {};
- let filePath;
let prerender = true;
let isolation = false;
let hydration = false;
@@ -115,9 +139,9 @@ 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 = id === 'index' || route.replace('/index', '') === `/${id}`
+ route = root === 'index' || route.replace('/index', '') === `/${root}`
? route.replace('index', '')
: `${route}/`;
} else {
@@ -127,60 +151,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;
- id = attributes.label || id;
+ 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));
@@ -213,11 +196,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
});
@@ -227,68 +207,99 @@ const generateGraph = async (compilation) => {
layout = ssrFrontmatter.layout || layout;
title = ssrFrontmatter.title || title;
imports = ssrFrontmatter.imports || imports;
- customData = ssrFrontmatter.data || 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 || '';
+ label = ssrFrontmatter.label || label;
+ customData = ssrFrontmatter || customData;
}
}
/*
- * Graph Properties (per page)
- *----------------------
- * 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
- * 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
- * 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)
- */
- pages.push({
+ * 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
+ [...activeFrontmatterKeys, 'layout'].forEach((key) => {
+ delete customData[key];
+ });
+
+ // 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);
+ }
+ }
+
+ /*
+ * Page Properties
+ *----------------------
+ * id: unique hyphen delimited string of the filename, relative to the pages directory
+ * 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
+ * outputHref: href to the file in the output folder
+ * pageHref: href to the page's filesystem file
+ * 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 = {
+ id: getIdFromRelativePathPath(relativePagePath, extension),
+ label,
+ title,
+ route: `${basePath}${route}`,
+ layout,
data: customData || {},
- filename,
- id,
- relativeWorkspacePagePath: relativePagePath,
- label: id.split('-')
- .map((idPart) => {
- return `${idPart.charAt(0).toUpperCase()}${idPart.substring(1)}`;
- }).join(' '),
imports,
resources: [],
- outputPath: route === '/404/'
- ? '/404.html'
- : `${route}index.html`,
- path: filePath,
- route: `${basePath}${route}`,
- layout,
- title,
+ pageHref: new URL(relativePagePath, pagesDir).href,
+ outputHref: route === '/404/'
+ ? new URL('./404.html', outputDir).href
+ : new URL(`.${route}index.html`, outputDir).href,
+ // outputPath: route === '/404/'
+ // ? '/404.html'
+ // : `${route}index.html`,
isSSR: !isStatic,
prerender,
isolation,
hydration,
servePage: isCustom
- });
+ };
+
+ pages.push(page);
+
+ // handle collections
+ 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}`);
+ console.warn(`Unsupported format detected for page => ${filename}`);
}
}
}
@@ -302,7 +313,7 @@ const generateGraph = async (compilation) => {
if (await checkResourceExists(new URL('./index.html', userWorkspace))) {
graph = [{
...graph[0],
- path: `${userWorkspace.pathname}index.html`,
+ pageHref: new URL('./index.html', userWorkspace).href,
isSPA: true
}];
} else {
@@ -325,12 +336,14 @@ const generateGraph = async (compilation) => {
...graph,
{
...oldGraph,
- outputPath: '/404.html',
- filename: '404.html',
+ id: '404',
+ // outputPath: '/404.html',
+ outputHref: new URL('./404.html', outputDir).href,
+ pageHref: new URL('./404.html', pagesDir).href,
route: `${basePath}/404/`,
path: '404.html',
- id: '404',
- label: 'Not Found'
+ label: 'Not Found',
+ title: 'Page Not Found'
}
];
}
@@ -352,12 +365,11 @@ const generateGraph = async (compilation) => {
}
graph.push({
- filename: null,
- path: null,
+ pageHref: null,
data: {},
imports: [],
resources: [],
- outputPath: `${node.route}index.html`,
+ outputHref: new URL(`.${node.route}index.html`, outputDir).href,
...node,
external: true
});
diff --git a/packages/cli/src/lifecycles/prerender.js b/packages/cli/src/lifecycles/prerender.js
index 67994a345..d67ffbce4 100644
--- a/packages/cli/src/lifecycles/prerender.js
+++ b/packages/cli/src/lifecycles/prerender.js
@@ -50,9 +50,15 @@ function getPluginInstances (compilation) {
});
}
+function toScratchUrl(outputHref, context) {
+ const { outputDir, scratchDir } = context;
+
+ return new URL(`./${outputHref.replace(outputDir.href, '')}`, scratchDir);
+}
+
async function preRenderCompilationWorker(compilation, workerPrerender) {
const pages = compilation.graph.filter(page => !page.isSSR || (page.isSSR && page.prerender) || (page.isSSR && compilation.config.prerender));
- const { scratchDir } = compilation.context;
+ const { context, config } = compilation;
const plugins = getPluginInstances(compilation);
console.info('pages to generate', `\n ${pages.map(page => page.route).join('\n ')}`);
@@ -60,9 +66,9 @@ async function preRenderCompilationWorker(compilation, workerPrerender) {
const pool = new WorkerPool(os.cpus().length, new URL('../lib/ssr-route-worker.js', import.meta.url));
for (const page of pages) {
- const { route, outputPath } = page;
- const outputPathUrl = new URL(`.${outputPath}`, scratchDir);
- const url = new URL(`http://localhost:${compilation.config.port}${route}`);
+ const { route, outputHref } = page;
+ const scratchUrl = toScratchUrl(outputHref, context);
+ const url = new URL(`http://localhost:${config.port}${route}`);
const request = new Request(url);
let ssrContents;
@@ -111,23 +117,23 @@ async function preRenderCompilationWorker(compilation, workerPrerender) {
body = body.replace('', ssrContents);
}
- await createOutputDirectory(route, new URL(outputPathUrl.href.replace('index.html', '')));
- await fs.writeFile(outputPathUrl, body);
+ await createOutputDirectory(route, new URL(scratchUrl.href.replace('index.html', '')));
+ await fs.writeFile(scratchUrl, body);
console.info('generated page...', route);
}
}
async function preRenderCompilationCustom(compilation, customPrerender) {
- const { scratchDir } = compilation.context;
+ const { config, context } = compilation;
const renderer = (await import(customPrerender.customUrl)).default;
- const { importMaps } = compilation.config.polyfills;
+ const { importMaps } = config.polyfills;
console.info('pages to generate', `\n ${compilation.graph.map(page => page.route).join('\n ')}`);
await renderer(compilation, async (page, body) => {
- const { route, outputPath } = page;
- const outputPathUrl = new URL(`.${outputPath}`, scratchDir);
+ const { route, outputHref } = page;
+ const scratchUrl = toScratchUrl(outputHref, context);
// clean up special Greenwood dev only assets that would come through if prerendering with a headless browser
if (importMaps) {
@@ -142,32 +148,32 @@ async function preRenderCompilationCustom(compilation, customPrerender) {
body = body.replace(/
+ `);
+
+ // Greenwood active frontmatter keys
+ for (const key of activeFrontmatterKeys) {
+ const interpolatedFrontmatter = '\\$\\{globalThis.page.' + key + '\\}';
+ const needle = key === 'title' && !matchingRoute.title
+ ? matchingRoute.label
+ : matchingRoute[key];
+
+ newBody = newBody.replace(new RegExp(interpolatedFrontmatter, 'g'), needle);
+ }
+
+ // custom user frontmatter data
+ for (const fm in matchingRoute.data) {
+ const interpolatedFrontmatter = '\\$\\{globalThis.page.data.' + fm + '\\}';
+ const needle = typeof matchingRoute.data[fm] === 'string' ? matchingRoute.data[fm] : JSON.stringify(matchingRoute.data[fm]).replace(/"/g, '"');
+
+ newBody = newBody.replace(new RegExp(interpolatedFrontmatter, 'g'), needle);
+ }
+
+ // collections
+ for (const collection in this.compilation.collections) {
+ const interpolatedFrontmatter = '\\$\\{globalThis.collection.' + collection + '\\}';
+ const cleanedCollections = cleanContentCollection(this.compilation.collections[collection]);
+
+ newBody = newBody.replace(new RegExp(interpolatedFrontmatter, 'g'), JSON.stringify(cleanedCollections).replace(/"/g, '"'));
+ }
+
+ return new Response(newBody);
+ }
+
+ async shouldOptimize(url, response) {
+ const { activeContent } = this.compilation.config;
+
+ return response.headers.get('Content-Type').indexOf(this.contentType[0]) >= 0 && activeContent;
+ }
+
+ async optimize(url, response) {
+ let body = await response.text();
+
+ body = body.replace('', `
+
+
+ `);
+
+ return new Response(body);
+ }
+}
+
+const greenwoodPluginContentAsData = {
+ type: 'resource',
+ name: 'plugin-active-content',
+ provider: (compilation) => new ContentAsDataResource(compilation)
+};
+
+export { greenwoodPluginContentAsData };
\ No newline at end of file
diff --git a/packages/cli/src/plugins/resource/plugin-node-modules.js b/packages/cli/src/plugins/resource/plugin-node-modules.js
index 286c2de08..76faabfef 100644
--- a/packages/cli/src/plugins/resource/plugin-node-modules.js
+++ b/packages/cli/src/plugins/resource/plugin-node-modules.js
@@ -10,7 +10,7 @@ import replace from '@rollup/plugin-replace';
import { getNodeModulesLocationForPackage, getPackageJson, getPackageNameFromUrl } from '../../lib/node-modules-utils.js';
import { resolveForRelativeUrl } from '../../lib/resource-utils.js';
import { ResourceInterface } from '../../lib/resource-interface.js';
-import { walkPackageJson } from '../../lib/walker-package-ranger.js';
+import { walkPackageJson, mergeImportMap } from '../../lib/walker-package-ranger.js';
let importMap;
@@ -75,7 +75,6 @@ class NodeModulesResource extends ResourceInterface {
async intercept(url, request, response) {
const { context, config } = this.compilation;
const { importMaps } = config.polyfills;
- const importMapType = importMaps ? 'importmap-shim' : 'importmap';
const importMapShimScript = importMaps ? '' : '';
let body = await response.text();
const hasHead = body.match(/\(.*)<\/head>/s);
@@ -97,15 +96,10 @@ class NodeModulesResource extends ResourceInterface {
? await walkPackageJson(userPackageJson)
: importMap || {};
- // apply import map and shim for users
+ body = mergeImportMap(body, importMap, importMaps);
body = body.replace('', `
${importMapShimScript}
-
`);
return new Response(body);
diff --git a/packages/cli/src/plugins/resource/plugin-standard-html.js b/packages/cli/src/plugins/resource/plugin-standard-html.js
index 06223cf3d..199379075 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';
@@ -37,18 +36,15 @@ class StandardHtmlResource extends ResourceInterface {
async serve(url, request) {
const { config, context } = this.compilation;
- const { pagesDir, userWorkspace } = context;
- const { interpolateFrontmatter } = config;
+ const { userWorkspace } = context;
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 { pageHref } = matchingRoute;
+ const filePath = !matchingRoute.external && pageHref ? new URL(pageHref).pathname.replace(userWorkspace.pathname, './') : '';
+ const isMarkdownContent = (filePath || '').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;
@@ -59,7 +55,7 @@ class StandardHtmlResource extends ResourceInterface {
}
if (isMarkdownContent) {
- const markdownContents = await fs.readFile(filePath, 'utf-8');
+ const markdownContents = await fs.readFile(new URL(pageHref), 'utf-8');
const rehypePlugins = [];
const remarkPlugins = [];
@@ -74,7 +70,6 @@ class StandardHtmlResource extends ResourceInterface {
}
const settings = config.markdown.settings || {};
- const fm = frontmatter(markdownContents);
processedMarkdown = await unified()
.use(remarkParse, settings) // parse markdown into AST
@@ -85,27 +80,10 @@ 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) {
- const routeModuleLocationUrl = new URL(`.${matchingRoute.relativeWorkspacePagePath}`, pagesDir);
+ const routeModuleLocationUrl = new URL(pageHref);
const routeWorkerUrl = this.compilation.config.plugins.find(plugin => plugin.type === 'renderer').provider().executeModuleUrl;
await new Promise(async (resolve, reject) => {
@@ -139,12 +117,12 @@ class StandardHtmlResource extends ResourceInterface {
}
if (isSpaRoute) {
- body = await fs.readFile(new URL(`./${isSpaRoute.filename}`, userWorkspace), 'utf-8');
+ body = await fs.readFile(new URL(isSpaRoute.pageHref), 'utf-8');
} else {
- body = ssrLayout ? ssrLayout : await getPageLayout(filePath, this.compilation, layout);
+ body = ssrLayout ? ssrLayout : await getPageLayout(pageHref, 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) {
@@ -171,15 +149,7 @@ class StandardHtmlResource extends ResourceInterface {
body = body.replace(/\(.*)<\/content-outlet>/s, `${ssrBody.replace(/\$/g, '$$$')}`);
}
- if (interpolateFrontmatter) {
- for (const fm in frontMatter) {
- const interpolatedFrontmatter = '\\$\\{globalThis.page.' + fm + '\\}';
-
- body = body.replace(new RegExp(interpolatedFrontmatter, 'g'), frontMatter[fm]);
- }
- }
-
- // clean up placeholder content-outlet
+ // clean up any empty placeholder content-outlet
if (body.indexOf('') > 0) {
body = body.replace('', '');
}
@@ -198,7 +168,7 @@ class StandardHtmlResource extends ResourceInterface {
async optimize(url, response) {
const { optimization, basePath } = this.compilation.config;
const { pathname } = url;
- const pageResources = this.compilation.graph.find(page => page.outputPath === pathname || page.route === pathname).resources;
+ const pageResources = this.compilation.graph.find(page => page.route === pathname).resources;
let body = await response.text();
const root = htmlparser.parse(body, {
diff --git a/packages/cli/src/plugins/resource/plugin-standard-json.js b/packages/cli/src/plugins/resource/plugin-standard-json.js
index 0610eb7ca..834db864a 100644
--- a/packages/cli/src/plugins/resource/plugin-standard-json.js
+++ b/packages/cli/src/plugins/resource/plugin-standard-json.js
@@ -17,22 +17,14 @@ class StandardJsonResource extends ResourceInterface {
async shouldServe(url) {
const { protocol, pathname } = url;
- const { basePath } = this.compilation.config;
const isJson = pathname.split('.').pop() === this.extensions[0];
- const isGraphJson = pathname === `${basePath}/graph.json`;
- const isWorkspaceFile = protocol === 'file:' && await checkResourceExists(url);
+ const isLocalFile = protocol === 'file:' && await checkResourceExists(url);
- return isJson && (isWorkspaceFile || isGraphJson);
+ return isJson && isLocalFile;
}
async serve(url) {
- const { pathname } = url;
- const { scratchDir } = this.compilation.context;
- const { basePath } = this.compilation.config;
- const finalUrl = pathname === `${basePath}/graph.json`
- ? new URL('./graph.json', scratchDir)
- : url;
- const contents = await fs.readFile(finalUrl, 'utf-8');
+ const contents = await fs.readFile(url, 'utf-8');
return new Response(contents, {
headers: new Headers({
diff --git a/packages/cli/src/plugins/resource/plugin-static-router.js b/packages/cli/src/plugins/resource/plugin-static-router.js
index 4145ec595..4975c1811 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.pageHref && page.pageHref.split('.').pop() === this.extensions[0]
? page.route
: page.layout;
const key = page.route === '/'
diff --git a/packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js b/packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js
new file mode 100644
index 000000000..b01574947
--- /dev/null
+++ b/packages/cli/test/cases/build.config.active-frontmatter/build.config.active-frontmatter.spec.js
@@ -0,0 +1,139 @@
+/*
+ * Use Case
+ * Run Greenwood with activeContent configuration enabled for validating active frontmatter.
+ *
+ * User Result
+ * Should generate a bare bones Greenwood build with correctly interpolated frontmatter variables in markdown and HTML.
+ *
+ * User Command
+ * greenwood build
+ *
+ * User Config
+ * {
+ * activeContent: true
+ * }
+ *
+ * User Workspace
+ * Greenwood default
+ * src/
+ * pages/
+ * blog/
+ * first-post.md
+ * second-post.md
+ * layouts/
+ * blog.html
+ */
+import { JSDOM } from 'jsdom';
+import path from 'path';
+import chai from 'chai';
+import { getSetupFiles, 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 = 'Active Frontmatter';
+ 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(function() {
+ runner.setup(outputPath, getSetupFiles(outputPath));
+ runner.runCommand(cliPath, 'build');
+ });
+
+ describe('Default Greenwood frontmatter should be interpolated in the correct places for the home page', function() {
+ let dom;
+
+ before(async function() {
+ dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './index.html'));
+ });
+
+ it('should have the correct value for the tag in the for the home page', function() {
+ const title = dom.window.document.querySelector('head title').textContent;
+
+ expect(title).to.be.equal('Home');
+ });
+
+ it('should have the correct graph value for id in a tag', function() {
+ const id = dom.window.document.querySelector('body span.id').textContent;
+
+ expect(id).to.be.equal('index');
+ });
+
+ it('should have the correct graph value for route in a tag', function() {
+ const route = dom.window.document.querySelector('body span.route').textContent;
+
+ expect(route).to.be.equal('/');
+ });
+
+ it('should have the correct graph value for label in a tag', function() {
+ const label = dom.window.document.querySelector('body span.label').textContent;
+
+ expect(label).to.be.equal('Home');
+ });
+ });
+
+ describe('Simple frontmatter should be interpolated in the correct places', function() {
+ let dom;
+
+ before(async function() {
+ dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './blog/first-post/index.html'));
+ });
+
+ it('should have the correct value for author tag in the ', function() {
+ const authorMeta = dom.window.document.querySelector('head meta[name=author]').getAttribute('content');
+
+ expect(authorMeta).to.be.equal('Owen Buckley');
+ });
+
+ it('should have the correct value for published in the
tag', function() {
+ const heading = dom.window.document.querySelector('body h3').textContent;
+
+ expect(heading).to.be.equal('Published: 11/11/2022');
+ });
+
+ it('should have the correct value for author in the
tag', function() {
+ const heading = dom.window.document.querySelector('body h4').textContent;
+
+ 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() {
+ runner.teardown(getOutputTeardownFiles(outputPath));
+ });
+});
\ No newline at end of file
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..99fe04d09
--- /dev/null
+++ b/packages/cli/test/cases/build.config.active-frontmatter/greenwood.config.js
@@ -0,0 +1,3 @@
+export default {
+ activeContent: 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 69%
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
index 58ce8c069..3377e2722 100644
--- 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
@@ -2,7 +2,7 @@
-
+
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 55%
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
index fa488585c..5f953abf6 100644
--- 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
@@ -7,7 +7,7 @@ author: Owen Buckley
# My First Post
-### Published: ${globalThis.page.published}
-#### Author: ${globalThis.page.author}
+### Published: ${globalThis.page.data.published}
+#### Author: ${globalThis.page.data.author}
Lorum Ipsum.
\ No newline at end of file
diff --git a/packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/second-post.md b/packages/cli/test/cases/build.config.active-frontmatter/src/pages/blog/second-post.md
new file mode 100644
index 000000000..b80a11dea
--- /dev/null
+++ b/packages/cli/test/cases/build.config.active-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.data.songs}
\ No newline at end of file
diff --git a/packages/cli/test/cases/build.config.active-frontmatter/src/pages/index.html b/packages/cli/test/cases/build.config.active-frontmatter/src/pages/index.html
new file mode 100644
index 000000000..2eddebcd2
--- /dev/null
+++ b/packages/cli/test/cases/build.config.active-frontmatter/src/pages/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+ ${globalThis.page.title}
+
+
+
+ ${globalThis.page.id}
+ ${globalThis.page.label}
+ ${globalThis.page.route}
+
+
+
\ No newline at end of file
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
deleted file mode 100644
index b39193067..000000000
--- a/packages/cli/test/cases/build.config.interpolate-frontmatter/build.config.interpolate-frontmatter.spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Use Case
- * Run Greenwood with interpolateFrontmatter configuration enabled.
- *
- * User Result
- * Should generate a bare bones Greenwood build with correctly interpolated frontmatter variables in markdown and HTML.
- *
- * User Command
- * greenwood build
- *
- * User Config
- * {
- * interpolateFrontmatter: true
- * }
- *
- * User Workspace
- * Greenwood default
- * src/
- * pages/
- * blog/
- * first-post.md
- * layouts/
- * blog.html
- */
-import { JSDOM } from 'jsdom';
-import path from 'path';
-import chai from 'chai';
-import { getSetupFiles, 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 = 'Frontmatter Interpolation';
- 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(function() {
- runner.setup(outputPath, getSetupFiles(outputPath));
- runner.runCommand(cliPath, 'build');
- });
-
- describe('Frontmatter should be interpolated in the correct places', function() {
- let dom;
-
- before(async function() {
- dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, './blog/first-post/index.html'));
- });
-
- it('should have the correct value for author tag in the ', function() {
- const authorMeta = dom.window.document.querySelector('head meta[name=author]').getAttribute('content');
-
- expect(authorMeta).to.be.equal('Owen Buckley');
- });
-
- it('should have the correct value for published in the
tag', function() {
- const heading = dom.window.document.querySelector('body h3').textContent;
-
- expect(heading).to.be.equal('Published: 11/11/2022');
- });
-
- it('should have the correct value for author in the
tag', function() {
- const heading = dom.window.document.querySelector('body h4').textContent;
-
- expect(heading).to.be.equal('Author: Owen Buckley');
- });
- });
- });
-
- after(function() {
- runner.teardown(getOutputTeardownFiles(outputPath));
- });
-});
\ No newline at end of file
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/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js b/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js
index c88c8cc7e..8edcd7de7 100644
--- a/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js
+++ b/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js
@@ -83,7 +83,7 @@ describe('Build Greenwood With: ', function() {
});
it('should have the expected
+
+
+
+
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..68a7cf2b4
--- /dev/null
+++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/index.html
@@ -0,0 +1,15 @@
+---
+collection: nav
+order: 1
+---
+
+
+
+
+
+
+
+ ${globalThis.collection.nav}
+
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..dea911cd1
--- /dev/null
+++ b/packages/cli/test/cases/build.config.prerender-collections/src/pages/toc.html
@@ -0,0 +1,15 @@
+---
+collection: nav
+order: 3
+label: Table of Contents
+---
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/cli/test/cases/develop.config.active-content/src/pages/blog/second-post.md b/packages/cli/test/cases/develop.config.active-content/src/pages/blog/second-post.md
new file mode 100644
index 000000000..ba4dc1e04
--- /dev/null
+++ b/packages/cli/test/cases/develop.config.active-content/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/develop.config.active-content/src/pages/contact.html b/packages/cli/test/cases/develop.config.active-content/src/pages/contact.html
new file mode 100644
index 000000000..fa7e87527
--- /dev/null
+++ b/packages/cli/test/cases/develop.config.active-content/src/pages/contact.html
@@ -0,0 +1,10 @@
+---
+collection: nav
+order: 4
+label: Contact
+---
+
+
+
Contact Page
+
+
\ No newline at end of file
diff --git a/packages/cli/test/cases/develop.config.active-content/src/pages/index.html b/packages/cli/test/cases/develop.config.active-content/src/pages/index.html
new file mode 100644
index 000000000..68a7cf2b4
--- /dev/null
+++ b/packages/cli/test/cases/develop.config.active-content/src/pages/index.html
@@ -0,0 +1,15 @@
+---
+collection: nav
+order: 1
+---
+
+
+
+
+
+
+
+ ${globalThis.collection.nav}
+
Home Page
+
+
\ No newline at end of file
diff --git a/packages/cli/test/cases/develop.config.active-content/src/pages/toc.html b/packages/cli/test/cases/develop.config.active-content/src/pages/toc.html
new file mode 100644
index 000000000..dea911cd1
--- /dev/null
+++ b/packages/cli/test/cases/develop.config.active-content/src/pages/toc.html
@@ -0,0 +1,15 @@
+---
+collection: nav
+order: 3
+label: Table of Contents
+---
+
+
+
+
+
+
+
tag', function() {
- const h2 = dom.window.document.querySelectorAll('body h2');
+ const heading = dom.window.document.querySelectorAll('photo-gallery h2');
- expect(h2.length).to.be.equal(1);
- expect(h2[0].textContent).to.be.equal(title);
+ expect(heading.length).to.be.equal(1);
+ expect(heading[0].textContent).to.be.equal(title);
});
});
});
diff --git a/packages/plugin-graphql/test/cases/query-custom-schema/src/pages/index.html b/packages/plugin-graphql/test/cases/query-custom-schema/src/pages/index.html
index 3d0ce48d9..d064c7120 100644
--- a/packages/plugin-graphql/test/cases/query-custom-schema/src/pages/index.html
+++ b/packages/plugin-graphql/test/cases/query-custom-schema/src/pages/index.html
@@ -3,33 +3,65 @@
-
-
-
-
+
\ No newline at end of file
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..386e390ff 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
@@ -185,14 +185,14 @@ 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 in the graph', function() {
const listItems = dom.window.document.querySelectorAll('body ul li');
expect(listItems.length).to.be.equal(4);
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/packages/plugin-graphql/test/cases/query-graph/src/pages/index.html b/packages/plugin-graphql/test/cases/query-graph/src/pages/index.html
index d1523ca91..be419b661 100644
--- a/packages/plugin-graphql/test/cases/query-graph/src/pages/index.html
+++ b/packages/plugin-graphql/test/cases/query-graph/src/pages/index.html
@@ -2,7 +2,7 @@
-
+
diff --git a/packages/plugin-graphql/test/cases/query-menu/greenwood.config.js b/packages/plugin-graphql/test/cases/query-menu/greenwood.config.js
deleted file mode 100644
index c38b0c8dd..000000000
--- a/packages/plugin-graphql/test/cases/query-menu/greenwood.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { greenwoodPluginGraphQL } from '../../../src/index.js';
-import { greenwoodPluginRendererPuppeteer } from '@greenwood/plugin-renderer-puppeteer';
-
-export default {
-
- plugins: [
- ...greenwoodPluginGraphQL(),
- ...greenwoodPluginRendererPuppeteer() // automatically invokes prerendering
- ]
-
-};
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/common.spec.js b/packages/plugin-graphql/test/unit/common.spec.js
index b50a96b44..94a3c2b99 100644
--- a/packages/plugin-graphql/test/unit/common.spec.js
+++ b/packages/plugin-graphql/test/unit/common.spec.js
@@ -14,11 +14,9 @@ describe('Unit Test: Data', function() {
const query = `
query {
graph {
- id,
title,
route,
path,
- filename,
layout,
__typename
}
@@ -26,7 +24,7 @@ describe('Unit Test: Data', function() {
`;
const hash = getQueryHash(query);
- expect(hash).to.be.equal('1291879437');
+ expect(hash).to.be.equal('309961297');
});
it('should return the expected hash for a custom graph query with custom data', function () {
@@ -51,11 +49,9 @@ describe('Unit Test: Data', function() {
const query = `
query($parent: String!) {
children(parent: $parent) {
- id,
title,
route,
path,
- filename,
layout
}
}
@@ -64,7 +60,7 @@ describe('Unit Test: Data', function() {
parent: '/docs/'
});
- expect(hash).to.be.equal('2106154137');
+ expect(hash).to.be.equal('1893453381');
});
});
diff --git a/packages/plugin-graphql/test/unit/mocks/config.js b/packages/plugin-graphql/test/unit/mocks/config.js
deleted file mode 100644
index b2a8a5ba8..000000000
--- a/packages/plugin-graphql/test/unit/mocks/config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const MOCK_CONFIG = {
- config: {
- devServer: {
- port: 1984
- },
- workspace: 'src'
- }
-};
-
-export {
- MOCK_CONFIG
-};
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/mocks/graph.js b/packages/plugin-graphql/test/unit/mocks/graph.js
index 9d15b7afb..75ae1e1d6 100644
--- a/packages/plugin-graphql/test/unit/mocks/graph.js
+++ b/packages/plugin-graphql/test/unit/mocks/graph.js
@@ -3,18 +3,18 @@ const MOCK_GRAPH = {
graph: [
{
data: {
- menu: "",
- index: "",
- linkheadings: 0,
+ collection: "",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./index.md",
- id: "index",
- label: "Index",
+ workspacePath: "./order.md",
+ id: "order",
+ label: "order",
route: "/",
layout: "home",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/index.md",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/order.md",
title: "",
meta: [
{
@@ -64,12 +64,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 3,
- linkheadings: 0,
+ collection: "side",
+ order: 3,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./about/community.md",
+ workspacePath: "./about/community.md",
id: "community",
label: "Community",
route: "/about/community",
@@ -125,12 +125,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 2,
- linkheadings: 0,
+ collection: "side",
+ order: 2,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./about/features.md",
+ workspacePath: "./about/features.md",
id: "features",
label: "Features",
route: "/about/features",
@@ -186,12 +186,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: "",
- linkheadings: 0,
+ collection: "side",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./about/goals.md",
+ workspacePath: "./about/goals.md",
id: "Goals",
label: "Goals",
route: "/about/goals",
@@ -247,9 +247,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 1,
- linkheadings: 3,
+ collection: "side",
+ order: 1,
+ tocHeading: 3,
tableOfContents: [
{
content: "CLI",
@@ -281,7 +281,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./about/how-it-works.md",
+ workspacePath: "./about/how-it-works.md",
id: "how-it-works",
label: "How It Works",
route: "/about/how-it-works",
@@ -337,18 +337,18 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "navigation",
- index: "",
- linkheadings: 0,
+ collection: "navigation",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./about/index.md",
+ workspacePath: "./about/order.md",
id: "about",
label: "About",
route: "/about/",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/about/index.md",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/about/order.md",
title: "About",
meta: [
{
@@ -398,12 +398,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 1,
- linkheadings: 0,
+ collection: "side",
+ order: 1,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./docs/component-model.md",
+ workspacePath: "./docs/component-model.md",
id: "component-model",
label: "Component Model",
route: "/docs/component-model",
@@ -459,9 +459,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 2,
- linkheadings: 3,
+ collection: "side",
+ order: 2,
+ tocHeading: 3,
tableOfContents: [
{
content: "Dev Server",
@@ -535,7 +535,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/configuration.md",
+ workspacePath: "./docs/configuration.md",
id: "configuration",
label: "Configuration",
route: "/docs/configuration",
@@ -591,9 +591,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 5,
- linkheadings: 3,
+ collection: "side",
+ order: 5,
+ tocHeading: 3,
tableOfContents: [
{
content: "Theming",
@@ -639,7 +639,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/css-and-images.md",
+ workspacePath: "./docs/css-and-images.md",
id: "css-and-images",
label: "CSS and Images",
route: "/docs/css-and-images",
@@ -695,9 +695,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 7,
- linkheadings: 3,
+ collection: "side",
+ order: 7,
+ tocHeading: 3,
tableOfContents: [
{
content: "Internal Sources",
@@ -749,8 +749,8 @@ const MOCK_GRAPH = {
seen: 0,
},
{
- content: "Menu Query",
- slug: "menu-query",
+ content: "collection Query",
+ slug: "collection-query",
lvl: 5,
i: 8,
seen: 0,
@@ -841,14 +841,14 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/data.md",
+ workspacePath: "./docs/data.md",
id: "data-sources",
label: "Data Sources",
route: "/docs/data",
layout: "page",
path:
"/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/docs/data.md",
- fileName: "data",
+ workspacePath: "data",
relativeExpectedPath: "'../docs/data/data.js'",
title: "Data Sources",
meta: [
@@ -899,9 +899,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 3,
- linkheadings: 3,
+ collection: "side",
+ order: 3,
+ tocHeading: 3,
tableOfContents: [
{
content: "Element Label",
@@ -975,7 +975,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/front-matter.md",
+ workspacePath: "./docs/front-matter.md",
id: "front-matter",
label: "Front Matter",
route: "/docs/front-matter",
@@ -1031,18 +1031,18 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "navigation",
- index: "",
- linkheadings: 0,
+ collection: "navigation",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./docs/index.md",
+ workspacePath: "./docs/order.md",
id: "docs",
label: "Docs",
route: "/docs/",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/docs/index.md",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/docs/order.md",
title: "Docs",
meta: [
{
@@ -1092,9 +1092,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 6,
- linkheadings: 3,
+ collection: "side",
+ order: 6,
+ tocHeading: 3,
tableOfContents: [
{
content: "Page Layout",
@@ -1126,7 +1126,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/layouts.md",
+ workspacePath: "./docs/layouts.md",
id: "layouts",
label: "Layouts",
route: "/docs/layouts",
@@ -1182,9 +1182,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 4,
- linkheadings: 3,
+ collection: "side",
+ order: 4,
+ tocHeading: 3,
tableOfContents: [
{
content: "Syntax Highlighting",
@@ -1209,7 +1209,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/markdown.md",
+ workspacePath: "./docs/markdown.md",
id: "markdown",
label: "Markdown",
route: "/docs/markdown",
@@ -1265,20 +1265,20 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 5,
- linkheadings: 3,
+ collection: "side",
+ order: 5,
+ tocHeading: 3,
tableOfContents: [
{
- content: "Declare Menu",
- slug: "declare-menu",
+ content: "Declare collection",
+ slug: "declare-collection",
lvl: 3,
i: 1,
seen: 0,
},
{
- content: "Retrieve Menu",
- slug: "retrieve-menu",
+ content: "Retrieve collection",
+ slug: "retrieve-collection",
lvl: 3,
i: 2,
seen: 0,
@@ -1299,14 +1299,14 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/menus.md",
- id: "menus",
- label: "Menus",
- route: "/docs/menus",
+ workspacePath: "./docs/collections.md",
+ id: "collections",
+ label: "collections",
+ route: "/docs/collections",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/docs/menus.md",
- title: "Menus",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/docs/collections.md",
+ title: "collections",
meta: [
{
name: "description",
@@ -1355,9 +1355,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 8,
- linkheadings: 3,
+ collection: "side",
+ order: 8,
+ tocHeading: 3,
tableOfContents: [
{
content: "NodeJS",
@@ -1389,7 +1389,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./docs/tech-stack.md",
+ workspacePath: "./docs/tech-stack.md",
id: "tech-stack",
label: "Tech Stack",
route: "/docs/tech-stack",
@@ -1445,9 +1445,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 5,
- linkheadings: 3,
+ collection: "side",
+ order: 5,
+ tocHeading: 3,
tableOfContents: [
{
content: "Web Components",
@@ -1465,7 +1465,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./getting-started/branding.md",
+ workspacePath: "./getting-started/branding.md",
id: "branding",
label: "Branding",
route: "/getting-started/branding",
@@ -1521,12 +1521,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 6,
- linkheadings: 0,
+ collection: "side",
+ order: 6,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./getting-started/build-and-deploy.md",
+ workspacePath: "./getting-started/build-and-deploy.md",
id: "build-and-deploy",
label: "Build And Deploy",
route: "/getting-started/build-and-deploy",
@@ -1582,9 +1582,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 4,
- linkheadings: 3,
+ collection: "side",
+ order: 4,
+ tocHeading: 3,
tableOfContents: [
{
content: "Objectives",
@@ -1623,7 +1623,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./getting-started/creating-content.md",
+ workspacePath: "./getting-started/creating-content.md",
id: "creating-content",
label: "Creating Content",
route: "/getting-started/creating-content",
@@ -1679,18 +1679,18 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "navigation",
- index: "",
- linkheadings: 0,
+ collection: "navigation",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./getting-started/index.md",
+ workspacePath: "./getting-started/order.md",
id: "getting-started",
label: "Getting Started",
route: "/getting-started/",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/getting-started/index.md",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/getting-started/order.md",
title: "Getting Started",
meta: [
{
@@ -1740,9 +1740,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 2,
- linkheadings: 3,
+ collection: "side",
+ order: 2,
+ tocHeading: 3,
tableOfContents: [
{
content: "Workspace",
@@ -1767,7 +1767,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./getting-started/key-concepts.md",
+ workspacePath: "./getting-started/key-concepts.md",
id: "key-concepts",
label: "Key Concepts",
route: "/getting-started/key-concepts",
@@ -1823,12 +1823,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 7,
- linkheadings: 0,
+ collection: "side",
+ order: 7,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./getting-started/next-steps.md",
+ workspacePath: "./getting-started/next-steps.md",
id: "next-steps",
label: "Next Steps",
route: "/getting-started/next-steps",
@@ -1884,9 +1884,9 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 2,
- linkheadings: 3,
+ collection: "side",
+ order: 2,
+ tocHeading: 3,
tableOfContents: [
{
content: "Installing Greenwood",
@@ -1911,7 +1911,7 @@ const MOCK_GRAPH = {
},
],
},
- filename: "./getting-started/project-setup.md",
+ workspacePath: "./getting-started/project-setup.md",
id: "project-setup",
label: "Project Setup",
route: "/getting-started/project-setup",
@@ -1967,12 +1967,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 1,
- linkheadings: 0,
+ collection: "side",
+ order: 1,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./getting-started/quick-start.md",
+ workspacePath: "./getting-started/quick-start.md",
id: "quick-start",
label: "Quick Start",
route: "/getting-started/quick-start",
@@ -2028,12 +2028,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 1,
- linkheadings: 0,
+ collection: "side",
+ order: 1,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./plugins/composite-plugins.md",
+ workspacePath: "./plugins/composite-plugins.md",
id: "composite-plugins",
label: "Composite Plugins",
route: "/plugins/composite-plugins",
@@ -2089,19 +2089,19 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 2,
- linkheadings: 0,
+ collection: "side",
+ order: 2,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./plugins/index-hooks.md",
- id: "index-hooks",
- label: "Index Hooks",
- route: "/plugins/index-hooks",
+ workspacePath: "./plugins/order-hooks.md",
+ id: "order-hooks",
+ label: "order Hooks",
+ route: "/plugins/order-hooks",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/plugins/index-hooks.md",
- title: "Index Hooks",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/plugins/order-hooks.md",
+ title: "order Hooks",
meta: [
{
name: "description",
@@ -2150,18 +2150,18 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "navigation",
- index: "",
- linkheadings: 0,
+ collection: "navigation",
+ order: "",
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./plugins/index.md",
+ workspacePath: "./plugins/order.md",
id: "plugins",
label: "Plugins",
route: "/plugins/",
layout: "page",
path:
- "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/plugins/index.md",
+ "/media/skynet/DATA/workspace/evergreen/greenwood/www/pages/plugins/order.md",
title: "Plugins",
meta: [
{
@@ -2211,12 +2211,12 @@ const MOCK_GRAPH = {
},
{
data: {
- menu: "side",
- index: 3,
- linkheadings: 0,
+ collection: "side",
+ order: 3,
+ tocHeading: 0,
tableOfContents: [],
},
- filename: "./plugins/webpack.md",
+ workspacePath: "./plugins/webpack.md",
id: "webpack",
label: "Webpack",
route: "/plugins/webpack",
diff --git a/packages/plugin-graphql/test/unit/schema/config.spec.js b/packages/plugin-graphql/test/unit/schema/config.spec.js
deleted file mode 100644
index ac1019b1d..000000000
--- a/packages/plugin-graphql/test/unit/schema/config.spec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import chai from 'chai';
-import { configResolvers } from '../../../src/schema/config.js';
-import { MOCK_CONFIG } from '../mocks/config.js';
-
-const expect = chai.expect;
-
-describe('Unit Test: Data', function() {
-
- describe('Schema', function() {
-
- describe('Config', function() {
- let config = {};
-
- before(async function() {
- config = await configResolvers.Query.config(undefined, {}, MOCK_CONFIG);
- });
-
- describe('Dev Server', function() {
- const { devServer } = MOCK_CONFIG.config;
-
- it('should have the expected devServer.port', function() {
- expect(config.devServer.port).to.equal(devServer.port);
- });
- });
-
- describe('Optimization', function() {
-
- it('should have a default optimization setting of default', function() {
- expect(config.optimization).to.equal(MOCK_CONFIG.config.optimization);
- });
-
- });
-
- describe('Prerender', function() {
-
- it('should have a default prerender setting of false', function() {
- expect(config.optimization).to.equal(MOCK_CONFIG.config.prerender);
- });
-
- });
-
- describe('Workspace', function() {
- const { workspace } = MOCK_CONFIG.config;
-
- it('should have the expected title', function() {
- expect(workspace).to.equal(config.workspace);
- });
- });
-
- });
-
- });
-});
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/schema/graph.children.spec.js b/packages/plugin-graphql/test/unit/schema/graph.children.spec.js
new file mode 100644
index 000000000..8bf3c4a8e
--- /dev/null
+++ b/packages/plugin-graphql/test/unit/schema/graph.children.spec.js
@@ -0,0 +1,85 @@
+import chai from 'chai';
+import { graphResolvers } from '../../../src/schema/graph.js';
+import { MOCK_GRAPH } from '../mocks/graph.js';
+
+const expect = chai.expect;
+
+describe('Unit Test: Data', function() {
+
+ describe('Schema', function() {
+
+ describe('Graph', function() {
+
+ describe('getChildrenFromGraph', function() {
+
+ describe('with default sort', function() {
+ let data = [];
+
+ before(async function() {
+ data = await graphResolvers.Query.children(undefined, {
+ parent: '/getting-started'
+ }, {
+ graph: MOCK_GRAPH.graph,
+ config: {
+ basePath: '/my-app'
+ }
+ });
+ });
+
+ it('should have 7 children', function() {
+ expect(data.length).to.equal(7);
+ });
+
+ it('should have Branding as the first item', function() {
+ const item = data[0];
+
+ expect(item.label).to.be.equal('Branding');
+ expect(item.route).to.be.equal('/getting-started/branding');
+ });
+
+ it('should have Build and Deploy as the second item', function() {
+ const item = data[1];
+
+ expect(item.label).to.be.equal('Build And Deploy');
+ expect(item.route).to.be.equal('/getting-started/build-and-deploy');
+ });
+
+ it('should have Creating Content as the third item', function() {
+ const item = data[2];
+
+ expect(item.label).to.be.equal('Creating Content');
+ expect(item.route).to.be.equal('/getting-started/creating-content');
+ });
+
+ it('should have Key Concepts as the fourth item', function() {
+ const item = data[3];
+
+ expect(item.label).to.be.equal('Key Concepts');
+ expect(item.route).to.be.equal('/getting-started/key-concepts');
+ });
+
+ it('should have Next Steps as the fifth item', function() {
+ const item = data[4];
+
+ expect(item.label).to.be.equal('Next Steps');
+ expect(item.route).to.be.equal('/getting-started/next-steps');
+ });
+
+ it('should have Project Setup as the sixth item', function() {
+ const item = data[5];
+
+ expect(item.label).to.be.equal('Project Setup');
+ expect(item.route).to.be.equal('/getting-started/project-setup');
+ });
+
+ it('should have Quick Start as the seventh item', function() {
+ const item = data[6];
+
+ expect(item.label).to.be.equal('Quick Start');
+ expect(item.route).to.be.equal('/getting-started/quick-start');
+ });
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/schema/graph.collection.spec.js b/packages/plugin-graphql/test/unit/schema/graph.collection.spec.js
new file mode 100644
index 000000000..68d67e67f
--- /dev/null
+++ b/packages/plugin-graphql/test/unit/schema/graph.collection.spec.js
@@ -0,0 +1,65 @@
+import chai from 'chai';
+import { graphResolvers } from '../../../src/schema/graph.js';
+import { MOCK_GRAPH } from '../mocks/graph.js';
+
+const expect = chai.expect;
+
+describe('Unit Test: Data', function() {
+
+ describe('Schema', function() {
+
+ describe('Graph', function() {
+
+ describe('getCollection navigation menu', function() {
+
+ describe('with default sort', function() {
+ let collection = [];
+
+ before(async function() {
+ collection = await graphResolvers.Query.collection(undefined, {
+ name: 'navigation'
+ }, {
+ graph: MOCK_GRAPH.graph,
+ config: {
+ basePath: ''
+ }
+ });
+ });
+
+ it('should have 4 children', function() {
+ expect(collection.length).to.equal(4);
+ });
+
+ it('should have About as the first item', function() {
+ const item = collection[0];
+
+ expect(item.label).to.be.equal('About');
+ expect(item.route).to.be.equal('/about/');
+ });
+
+ it('should have Docs as the second item', function() {
+ const item = collection[1];
+
+ expect(item.label).to.be.equal('Docs');
+ expect(item.route).to.be.equal('/docs/');
+ });
+
+ it('should have Getting Started as the third item', function() {
+ const item = collection[2];
+
+ expect(item.label).to.be.equal('Getting Started');
+ expect(item.route).to.be.equal('/getting-started/');
+ });
+
+ it('should have Plugins as the fourth item', function() {
+ const item = collection[3];
+
+ expect(item.label).to.be.equal('Plugins');
+ expect(item.route).to.be.equal('/plugins/');
+ });
+ });
+ });
+
+ });
+ });
+});
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/schema/graph.menu.spec.js b/packages/plugin-graphql/test/unit/schema/graph.menu.spec.js
deleted file mode 100644
index d1d6a2abd..000000000
--- a/packages/plugin-graphql/test/unit/schema/graph.menu.spec.js
+++ /dev/null
@@ -1,456 +0,0 @@
-import chai from 'chai';
-import { graphResolvers } from '../../../src/schema/graph.js';
-import { MOCK_GRAPH } from '../mocks/graph.js';
-
-const expect = chai.expect;
-
-describe('Unit Test: Data', function() {
-
- describe('Schema', function() {
-
- describe('Graph', function() {
-
- describe('getMenuFromGraph navigation menu', function() {
-
- describe('with default sort', function() {
- let navigation = [];
-
- before(async function() {
- navigation = await graphResolvers.Query.menu(undefined, {
- name: 'navigation'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: ''
- }
- });
- });
-
- it('should have 4 children', function() {
- expect(navigation.children.length).to.equal(4);
- });
-
- it('should have About as the first item', function() {
- const item = navigation.children[0].item;
-
- expect(item.label).to.be.equal('About');
- expect(item.route).to.be.equal('/about/');
- });
-
- it('should have Docs as the second item', function() {
- const item = navigation.children[1].item;
-
- expect(item.label).to.be.equal('Docs');
- expect(item.route).to.be.equal('/docs/');
- });
-
- it('should have Getting Started as the third item', function() {
- const item = navigation.children[2].item;
-
- expect(item.label).to.be.equal('Getting Started');
- expect(item.route).to.be.equal('/getting-started/');
- });
-
- it('should have Plugins as the fourth item', function() {
- const item = navigation.children[3].item;
-
- expect(item.label).to.be.equal('Plugins');
- expect(item.route).to.be.equal('/plugins/');
- });
- });
- });
-
- describe('getMenuFromGraph filtering by side menu from path /getting-started', function() {
- describe('with no sorting (default)', function() {
- let shelf = [];
-
- before(async function() {
- shelf = await graphResolvers.Query.menu(undefined, {
- pathname: '/getting-started/',
- name: 'side'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: '/my-app'
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(shelf.children.length).to.equal(7);
- });
-
- describe('the first item:', function() {
- it('should be labeled and linked to Styles and Web Components', function() {
- const item = shelf.children[0].item;
-
- expect(item.label).to.be.equal('Branding');
- expect(item.route).to.be.equal('/getting-started/branding');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[0].children;
-
- expect(subitem[0].item.label).to.be.equal('Web Components');
- expect(subitem[0].item.route).to.be.equal('#web-components');
- expect(subitem[1].item.label).to.be.equal('CSS');
- expect(subitem[1].item.route).to.be.equal('#css');
- });
- });
-
- describe('the second item:', function() {
- it('should be labeled and linked to Build And Deploy', function() {
- const item = shelf.children[1].item;
-
- expect(item.label).to.be.equal('Build And Deploy');
- expect(item.route).to.be.equal('/getting-started/build-and-deploy');
- });
- });
-
- describe('the third item:', function() {
- it('should be labeled and linked to Creating Content', function() {
- const item = shelf.children[2].item;
-
- expect(item.label).to.be.equal('Creating Content');
- expect(item.route).to.be.equal('/getting-started/creating-content');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[2].children;
- expect(subitem[0].item.label).to.be.equal('Objectives');
- expect(subitem[1].item.label).to.be.equal('Home Page Layout');
- expect(subitem[2].item.label).to.be.equal('Blog Posts Layout');
- expect(subitem[3].item.label).to.be.equal('Creating Pages');
- expect(subitem[4].item.label).to.be.equal('Development Server');
- });
- });
- });
-
- describe('with custom front matter index ascending', function() {
- let shelf = [];
-
- before(async function() {
- shelf = await graphResolvers.Query.menu(undefined, {
- pathname: '/getting-started/',
- name: 'side',
- orderBy: 'index_asc'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: ''
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(shelf.children.length).to.equal(7);
- });
-
- describe('the first item:', function() {
- it('should be labeled and linked to Quick Start', function() {
- const item = shelf.children[0].item;
-
- expect(item.label).to.be.equal('Quick Start');
- expect(item.route).to.be.equal('/getting-started/quick-start');
- });
- });
-
- describe('the second item:', function() {
- it('should be labeled and linked to Key Concepts', function() {
- const item = shelf.children[1].item;
-
- expect(item.label).to.be.equal('Key Concepts');
- expect(item.route).to.be.equal('/getting-started/key-concepts');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[1].children;
-
- expect(subitem[0].item.label).to.be.equal('Workspace');
- expect(subitem[0].item.route).to.be.equal('#workspace');
- expect(subitem[1].item.label).to.be.equal('Layouts');
- expect(subitem[1].item.route).to.be.equal('#layouts');
- expect(subitem[2].item.label).to.be.equal('Pages');
- expect(subitem[2].item.route).to.be.equal('#pages');
- });
- });
-
- describe('the third item:', function() {
- it('should be labeled and linked to Project Setup', function() {
- const item = shelf.children[2].item;
-
- expect(item.label).to.be.equal('Project Setup');
- expect(item.route).to.be.equal('/getting-started/project-setup');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[2].children;
-
- expect(subitem[0].item.label).to.be.equal('Installing Greenwood');
- expect(subitem[0].item.route).to.be.equal('#installing-greenwood');
- expect(subitem[1].item.label).to.be.equal('Configuring Workflows');
- expect(subitem[1].item.route).to.be.equal('#configuring-workflows');
- expect(subitem[2].item.label).to.be.equal('Project Structure');
- expect(subitem[2].item.route).to.be.equal('#project-structure');
- });
- });
- });
-
- describe('with custom front matter index descending', function() {
- let shelf = [];
-
- before(async function() {
- shelf = await graphResolvers.Query.menu(undefined, {
- pathname: '/getting-started/',
- name: 'side',
- orderBy: 'index_desc'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: ''
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(shelf.children.length).to.equal(7);
- });
-
- describe('the first item:', function() {
- it('should be labeled and linked to Next Steps', function() {
- const item = shelf.children[0].item;
-
- expect(item.label).to.be.equal('Next Steps');
- expect(item.route).to.be.equal('/getting-started/next-steps');
- });
- });
-
- describe('the second item:', function() {
- it('should be labeled and linked to Build And Deploy', function() {
- const item = shelf.children[1].item;
-
- expect(item.label).to.be.equal('Build And Deploy');
- expect(item.route).to.be.equal('/getting-started/build-and-deploy');
- });
- });
-
- describe('the third item:', function() {
- it('should be labeled and linked to Styles and Web Components', function() {
- const item = shelf.children[2].item;
-
- expect(item.label).to.be.equal('Branding');
- expect(item.route).to.be.equal('/getting-started/branding');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[2].children;
-
- expect(subitem[0].item.label).to.be.equal('Web Components');
- expect(subitem[0].item.route).to.be.equal('#web-components');
- expect(subitem[1].item.label).to.be.equal('CSS');
- expect(subitem[1].item.route).to.be.equal('#css');
- });
- });
- });
-
- describe('with custom front matter title ascending', function() {
- let shelf = [];
-
- before(async function() {
- shelf = await graphResolvers.Query.menu(undefined, {
- pathname: '/getting-started/',
- name: 'side',
- orderBy: 'title_asc'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: ''
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(shelf.children.length).to.equal(7);
- });
-
- describe('the first item:', function() {
- it('should be labeled and linked to Branding', function() {
- const item = shelf.children[0].item;
-
- expect(item.label).to.be.equal('Branding');
- expect(item.route).to.be.equal('/getting-started/branding');
- });
- });
-
- describe('the second item:', function() {
- it('should be labeled and linked to Build And Deploy', function() {
- const item = shelf.children[1].item;
-
- expect(item.label).to.be.equal('Build And Deploy');
- expect(item.route).to.be.equal('/getting-started/build-and-deploy');
- });
- });
-
- describe('the third item:', function() {
- it('should be labeled and linked to Creating Content', function() {
- const item = shelf.children[2].item;
-
- expect(item.label).to.be.equal('Creating Content');
- expect(item.route).to.be.equal('/getting-started/creating-content');
- });
- });
-
- describe('the fourth item:', function() {
- it('should be labeled and linked to Key Concepts', function() {
- const item = shelf.children[3].item;
-
- expect(item.label).to.be.equal('Key Concepts');
- expect(item.route).to.be.equal('/getting-started/key-concepts');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[3].children;
-
- expect(subitem[0].item.label).to.be.equal('Workspace');
- expect(subitem[0].item.route).to.be.equal('#workspace');
- expect(subitem[1].item.label).to.be.equal('Layouts');
- expect(subitem[1].item.route).to.be.equal('#layouts');
- expect(subitem[2].item.label).to.be.equal('Pages');
- expect(subitem[2].item.route).to.be.equal('#pages');
- });
- });
- });
-
- describe('with custom front matter title descending', function() {
- let shelf = [];
-
- before(async function() {
- shelf = await graphResolvers.Query.menu(undefined, {
- pathname: '/getting-started/',
- name: 'side',
- orderBy: 'title_desc'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: ''
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(shelf.children.length).to.equal(7);
- });
-
- describe('the first item:', function() {
- it('should be labeled and linked to Quick Start', function() {
- const item = shelf.children[0].item;
-
- expect(item.label).to.be.equal('Quick Start');
- expect(item.route).to.be.equal('/getting-started/quick-start');
- });
- });
-
- describe('the second item:', function() {
- it('should be labeled and linked to Project Setup', function() {
- const item = shelf.children[1].item;
-
- expect(item.label).to.be.equal('Project Setup');
- expect(item.route).to.be.equal('/getting-started/project-setup');
- });
-
- it('should have the correct sub items', function() {
- const subitem = shelf.children[1].children;
-
- expect(subitem[0].item.label).to.be.equal('Installing Greenwood');
- expect(subitem[0].item.route).to.be.equal('#installing-greenwood');
- expect(subitem[1].item.label).to.be.equal('Configuring Workflows');
- expect(subitem[1].item.route).to.be.equal('#configuring-workflows');
- expect(subitem[2].item.label).to.be.equal('Project Structure');
- expect(subitem[2].item.route).to.be.equal('#project-structure');
- });
- });
-
- describe('the third item:', function() {
- it('should be labeled and linked to Next Steps', function() {
- const item = shelf.children[2].item;
-
- expect(item.label).to.be.equal('Next Steps');
- expect(item.route).to.be.equal('/getting-started/next-steps');
- });
- });
- });
- });
-
- describe('getChildrenFromGraph', function() {
-
- describe('with default sort', function() {
- let data = [];
-
- before(async function() {
- data = await graphResolvers.Query.children(undefined, {
- parent: '/getting-started'
- }, {
- graph: MOCK_GRAPH.graph,
- config: {
- basePath: '/my-app'
- }
- });
- });
-
- it('should have 7 children', function() {
- expect(data.length).to.equal(7);
- });
-
- it('should have Branding as the first item', function() {
- const item = data[0];
-
- expect(item.label).to.be.equal('Branding');
- expect(item.route).to.be.equal('/getting-started/branding');
- });
-
- it('should have Build and Deploy as the second item', function() {
- const item = data[1];
-
- expect(item.label).to.be.equal('Build And Deploy');
- expect(item.route).to.be.equal('/getting-started/build-and-deploy');
- });
-
- it('should have Creating Content as the third item', function() {
- const item = data[2];
-
- expect(item.label).to.be.equal('Creating Content');
- expect(item.route).to.be.equal('/getting-started/creating-content');
- });
-
- it('should have Key Concepts as the fourth item', function() {
- const item = data[3];
-
- expect(item.label).to.be.equal('Key Concepts');
- expect(item.route).to.be.equal('/getting-started/key-concepts');
- });
-
- it('should have Next Steps as the fifth item', function() {
- const item = data[4];
-
- expect(item.label).to.be.equal('Next Steps');
- expect(item.route).to.be.equal('/getting-started/next-steps');
- });
-
- it('should have Project Setup as the sixth item', function() {
- const item = data[5];
-
- expect(item.label).to.be.equal('Project Setup');
- expect(item.route).to.be.equal('/getting-started/project-setup');
- });
-
- it('should have Quick Start as the seventh item', function() {
- const item = data[6];
-
- expect(item.label).to.be.equal('Quick Start');
- expect(item.route).to.be.equal('/getting-started/quick-start');
- });
- });
- });
- });
- });
-});
\ No newline at end of file
diff --git a/packages/plugin-graphql/test/unit/schema/graph.spec.js b/packages/plugin-graphql/test/unit/schema/graph.spec.js
index e387592bd..f38905699 100644
--- a/packages/plugin-graphql/test/unit/schema/graph.spec.js
+++ b/packages/plugin-graphql/test/unit/schema/graph.spec.js
@@ -24,9 +24,6 @@ describe('Unit Test: Data', function() {
it('should have all expected properties for each page', function() {
pages.forEach(function(page) {
expect(page.label).to.exist;
- expect(page.id).to.exist;
- expect(page.path).to.exist;
- expect(page.filename).to.exist;
expect(page.layout).to.exist;
expect(page.title).to.exist;
expect(page.route).to.exist;
@@ -50,20 +47,14 @@ describe('Unit Test: Data', function() {
expect(children.length).to.equal(7);
});
- it('should have the expected value for id for each child', function() {
- expect(children[0].id).to.equal('branding');
- expect(children[1].id).to.equal('build-and-deploy');
- expect(children[2].id).to.equal('creating-content');
- expect(children[3].id).to.equal('key-concepts');
- expect(children[4].id).to.equal('next-steps');
- expect(children[5].id).to.equal('project-setup');
- expect(children[6].id).to.equal('quick-start');
- });
-
it('should have the expected route for each child', function() {
- children.forEach(function(child) {
- expect(child.route).to.equal(`/getting-started/${child.id}`);
- });
+ expect(children[0].route).to.equal('/getting-started/branding');
+ expect(children[1].route).to.equal('/getting-started/build-and-deploy');
+ expect(children[2].route).to.equal('/getting-started/creating-content');
+ expect(children[3].route).to.equal('/getting-started/key-concepts');
+ expect(children[4].route).to.equal('/getting-started/next-steps');
+ expect(children[5].route).to.equal('/getting-started/project-setup');
+ expect(children[6].route).to.equal('/getting-started/quick-start');
});
it('should have the expected label for each child', function() {
@@ -76,12 +67,6 @@ describe('Unit Test: Data', function() {
expect(children[6].label).to.equal('Quick Start');
});
- it('should have the expected path for each child', function() {
- children.forEach(function(child) {
- expect(child.path).to.contain(`/getting-started/${child.id}.md`);
- });
- });
-
it('should have "page" as the layout for all children', function() {
children.forEach(function(child) {
expect(child.layout).to.equal('page');
@@ -99,7 +84,7 @@ describe('Unit Test: Data', function() {
});
it('should have expected custom front matter data if it is set', function() {
- expect(children[0].data.menu).to.equal('side');
+ expect(children[0].data.collection).to.equal('side');
});
});
});
diff --git a/packages/plugin-renderer-lit/test/cases/serve.default/serve.default.spec.js b/packages/plugin-renderer-lit/test/cases/serve.default/serve.default.spec.js
index e65bd24a9..d90ffc1cf 100644
--- a/packages/plugin-renderer-lit/test/cases/serve.default/serve.default.spec.js
+++ b/packages/plugin-renderer-lit/test/cases/serve.default/serve.default.spec.js
@@ -178,13 +178,13 @@ describe('Serve Greenwood With: ', function() {
let dom;
let usersPageDom;
let usersPageHtml;
- let aboutPageGraphData;
+ let artistsPageGraphData;
before(async function() {
const graph = JSON.parse(await fs.promises.readFile(path.join(outputPath, 'public/graph.json'), 'utf-8'));
artists = JSON.parse(await fs.promises.readFile(new URL('./artists.json', import.meta.url), 'utf-8'));
- aboutPageGraphData = graph.filter(page => page.route === '/artists/')[0];
+ artistsPageGraphData = graph.filter(page => page.route === '/artists/')[0];
response = await fetch(`${hostname}/artists/`);
data = await response.text();
@@ -252,17 +252,17 @@ describe('Serve Greenwood With: ', function() {
});
it('should be a part of graph.json', function() {
- expect(aboutPageGraphData).to.not.be.undefined;
+ expect(artistsPageGraphData).to.not.be.undefined;
});
it('should have the expected menu and index values in the graph', function() {
- expect(aboutPageGraphData.data.menu).to.equal('navigation');
- expect(aboutPageGraphData.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() {
- expect(aboutPageGraphData.data.author).to.equal('Project Evergreen');
- expect(aboutPageGraphData.data.date).to.equal('01-01-2021');
+ expect(artistsPageGraphData.data.author).to.equal('Project Evergreen');
+ expect(artistsPageGraphData.data.date).to.equal('01-01-2021');
});
it('should not have the expected lit hydration script in the ', function() {
diff --git a/packages/plugin-renderer-lit/test/cases/serve.default/src/pages/artists.js b/packages/plugin-renderer-lit/test/cases/serve.default/src/pages/artists.js
index 19c96e771..5125c5aaf 100644
--- a/packages/plugin-renderer-lit/test/cases/serve.default/src/pages/artists.js
+++ b/packages/plugin-renderer-lit/test/cases/serve.default/src/pages/artists.js
@@ -67,13 +67,11 @@ async function getBody() {
async function getFrontmatter(compilation, { route }) {
return {
- menu: 'navigation',
- index: 7,
+ collection: 'navigation',
+ order: 7,
title: `My App - ${route}`,
- data: {
- author: 'Project Evergreen',
- date: '01-01-2021'
- }
+ author: 'Project Evergreen',
+ date: '01-01-2021'
};
}
diff --git a/packages/plugin-renderer-puppeteer/src/plugins/server.js b/packages/plugin-renderer-puppeteer/src/plugins/server.js
index 0a2c62530..ea8232557 100644
--- a/packages/plugin-renderer-puppeteer/src/plugins/server.js
+++ b/packages/plugin-renderer-puppeteer/src/plugins/server.js
@@ -10,9 +10,10 @@ class PuppeteerServer extends ServerInterface {
async start() {
if (process.env.__GWD_COMMAND__ === 'build') { // eslint-disable-line no-underscore-dangle
const { port } = this.compilation.config.devServer;
+ const offsetPort = port + 1; // don't try and start the dev server on the same port as the CLI
- (await getDevServer(this.compilation)).listen(port, async () => {
- console.info(`Started puppeteer prerender server at http://localhost:${port}`);
+ (await getDevServer(this.compilation)).listen(offsetPort, async () => {
+ console.info(`Started puppeteer prerender server at http://localhost:${offsetPort}`);
});
} else {
await Promise.resolve();
diff --git a/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js b/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js
index 0a0b7d3d1..ef8945883 100644
--- a/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js
+++ b/packages/plugin-renderer-puppeteer/src/puppeteer-handler.js
@@ -50,7 +50,8 @@ export default async function(compilation, callback) {
try {
const pages = compilation.graph.filter(page => !page.isSSR);
const port = compilation.config.devServer.port;
- const serverAddress = `http://127.0.0.1:${port}`;
+ const offsetPort = port + 1; // don't try and start the dev server on the same port as the CLI
+ const serverAddress = `http://127.0.0.1:${offsetPort}`;
await runBrowser(serverAddress, pages);
browserRunner.close();
diff --git a/www/components/header/header.js b/www/components/header/header.js
index 4cc34950a..27e85f856 100644
--- a/www/components/header/header.js
+++ b/www/components/header/header.js
@@ -1,6 +1,6 @@
import { css, html, LitElement, unsafeCSS } 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';
import headerCss from './header.css?type=raw';
import '../social-icons/social-icons.js';
@@ -29,14 +29,14 @@ class HeaderComponent extends LitElement {
super.connectedCallback();
const response = await client.query({
- query: MenuQuery,
+ query: CollectionQuery,
variables: {
name: 'navigation',
- order: 'index_asc'
+ orderBy: 'order_asc'
}
});
- this.navigation = response.data.menu.children.map(item => item.item);
+ this.navigation = response.data.collection;
}
/* eslint-disable indent */
@@ -61,8 +61,8 @@ class HeaderComponent extends LitElement {
`;
}
}
-
-customElements.define('app-header', HeaderComponent);
```
-> _For more information on using GraphQL with Greenwood, please see our [GraphQL plugin's README](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-graphql)._
-### External Sources
+### GraphQL
-Using our [Source plugin](/plugins/source/), just as you can get your content as data _out_ of Greenwood, so can you provide your own sources of data _to_ Greenwood. This is great for pulling content from a headless CMS, database, or anything else you can imagine!
+For GraphQL support, please see our [**GraphQL plugin**](https://github.com/ProjectEvergreen/greenwood/tree/master/packages/plugin-graphql) which in additional to exposing an [Apollo server and playground](https://www.apollographql.com/docs/apollo-server/) locally at `http://localhost:4000`, also provides GraphQL alternatives to our Data Client through a customized (read only) Apollo client based wrapper.
-The supported [fields from Greenwood's schema](/docs/data/#internal-sources) are:
-```javascript
-graph {
- body, // REQUIRED (string of your content)
- id,
- label,
- route, // REQUIRED and MUST end in a forward slash
- layout,
- title,
- data
-}
-```
\ No newline at end of file
+
\ No newline at end of file
diff --git a/www/pages/docs/front-matter.md b/www/pages/docs/front-matter.md
index c86114135..5be461449 100644
--- a/www/pages/docs/front-matter.md
+++ b/www/pages/docs/front-matter.md
@@ -1,16 +1,15 @@
---
-label: 'front-matter'
-menu: side
-title: 'Front Matter'
-index: 3
-linkheadings: 3
+collection: docs
+title: Front Matter
+order: 3
+tocHeading: 3
---
## Front Matter
"Front matter" is a [YAML](https://yaml.org/) block at the top of any markdown file. It gives you the ability to define variables that are made available to Greenwood's build process and then your code. You can also use it to `import` additional files.
-### Element Label
+### Label
By default Greenwood will aim to create a label for your page based on filename and context and include that in the graph. This can be useful for categorizing or organizing your content when rendering client side, or if you want to create a custom value to display for a link or in your HTML that may be different from what can be inferred from the file name.
@@ -23,7 +22,6 @@ label: 'My Blog Post from 3/5/2020'
```
-
### Imports
If you want to include files on a _per **page** basis_, you can use the predefined `imports` feature from Greenwood. This is great for one off use cases where you don't want to ship a third party lib in all your layouts, but just for this one particular page. This is effectively a naive form of code splitting. 🤓
@@ -46,7 +44,6 @@ You will then see the following emitted for file
> _See our [Markdown Docs](/docs/markdown#imports) for more information about rendering custom elements in markdown files._
-
### Layouts
When creating multiple [page layouts](/docs/layouts/), you can use the `layout` front-matter to configure Greenwood to use that layout for a given page.
@@ -83,34 +80,35 @@ In this example, the `` tag will be the `title`.
My Blog Post
```
-> Note: If you set `title` from your [configuration file](/docs/configuration#title), the output would be
-> ```html
-> {ConfigTitle} - My Blog Post
-> ```
-
### Custom Data
-You can also pass custom data from your markdown file and extract that from Greenwood's [_graph.json_ via `fetch` or our GraphQL server](/docs/data/).
+You can also pass custom data from your markdown file and that will be made available to Greenwood's [content as data](/docs/data/) or [active frontmatter](/docs/configuration/#active-frontmatter) capabilities.
#### Example
```md
---
-author: 'Jon Doe'
-date: '04/07/2020'
+author: Jon Doe
+date: 04/07/2020'
---
-```
-You would then need to create a `graph` GraphQL query and use that with Greenwood's built in client to get access to that `data`, plus whatever other fields you might want.
-```graphql
-query {
- graph {
- data {
- author,
- date
- }
- }
-}
+# First Post
+
+My first post
```
-> See [our docs](/docs/data#internal-sources) on using GraphQL w/Greenwood for more information on querying for data.
\ No newline at end of file
+Would then be available in the [`data` property](/docs/data/#page-data).
+
+### Active Frontmatter
+
+With [`activeFrontmatter`](/docs/configuration/#active-frontmatter) enabled, any of these properties would be available in your HTML or markdown.
+
+```md
+---
+author: Project Evergreen
+---
+
+## My Post
+
+Authored By: ${globalThis.page.data.author}
+```
\ No newline at end of file
diff --git a/www/pages/docs/index.md b/www/pages/docs/index.md
index 9ae7e0dad..187d79a05 100644
--- a/www/pages/docs/index.md
+++ b/www/pages/docs/index.md
@@ -1,8 +1,6 @@
---
-label: 'docs'
-menu: navigation
-title: Docs
-index: 2
+collection: navigation
+order: 2
---
## Documentation
diff --git a/www/pages/docs/layouts.md b/www/pages/docs/layouts.md
index 4f863b030..498f8469d 100644
--- a/www/pages/docs/layouts.md
+++ b/www/pages/docs/layouts.md
@@ -1,9 +1,9 @@
---
-label: 'layouts-and-pages'
-menu: side
-title: 'Layouts and Pages'
-index: 7
-linkheadings: 3
+collection: docs
+title: Layouts and Pages
+label: Layouts and Pages
+order: 7
+tocHeading: 3
---
## Layouts and Pages
diff --git a/www/pages/docs/markdown.md b/www/pages/docs/markdown.md
index 05e1b8c38..262298fc8 100644
--- a/www/pages/docs/markdown.md
+++ b/www/pages/docs/markdown.md
@@ -1,9 +1,7 @@
---
-label: 'markdown'
-menu: side
-title: 'Markdown'
-index: 4
-linkheadings: 3
+collection: docs
+order: 4
+tocHeading: 3
---
## Markdown
diff --git a/www/pages/docs/menus.md b/www/pages/docs/menus.md
deleted file mode 100644
index bd473da0e..000000000
--- a/www/pages/docs/menus.md
+++ /dev/null
@@ -1,295 +0,0 @@
----
-label: 'menus'
-menu: side
-title: 'Menus'
-index: 10
-linkheadings: 3
----
-
-## Menus
-
-In this section we'll touch on the menu related feature of Greenwood which utilizes [data sources](/docs/data/) within a component to query for [front matter](/docs/front-matter/) declared menus.
-
-### Declare Menu
-
-A common example of a menu you might use would be a **navigation** menu.
-
-To do this we first need to define which pages will be linked in this navigation menu.
-
-For this example, let's say we want "about", "docs", "contact us", all linked within our navigation menu. Then we need to define the navigation menu, within the [front matter](/docs/front-matter) at the top of each page. The front matter defined variables for menus are:
-
-| Variable | Description |
-|-------------|:--------------------------------------------------|
-| title | The title of the page link within the menu |
-| menu | The name of the menu, cannot have spaces or special characters. |
-| index | The position of the page within a menu. Custom set the position higher or lower than default. You can sort these positions alphabetically or by index |
-| linkheadings | Integer. If you want to parse the page for headings and include them as children of the page link, add `linkheadings: 3` to parse for `
` headings. Set integer to the heading level you want to parse. e.g. `h1, h2, h3` |
-
-e.g. create the following in a new directory within your `/pages` directory.
-
-`index.md`
-
-```md
----
-title: 'About'
-menu: 'navigation'
-index: 1
----
-
-# About
-```
-
-
-`docs.md`
-
-```md
----
-title: 'Docs'
-menu: 'navigation'
-index: 2
----
-
-# Documentation
-```
-
-`contact.md`
-
-```md
----
-title: 'Contact'
-menu: 'navigation'
-index: 3
-linkheadings: 3
----
-
-# Contact
-
-### Online
-
-### Offline
-
-### Locations
-```
-
-> **Note:** the front-matter variable `linkheadings: 3` will add all the `
` headings as children subitems within a menu item. So in this example the menu item `Contact`, will have the children: `Online`(linked to #online), `Offline`(linked to #offline), and `Locations`(linked to #locations). You can set `linkheadings:` to any header level you require not just `3` e.g. `linkheadings: 2` for `
` elements. An example of the [linkheadings query result](#query-result) can be found below.
-
-### Retrieve Menu
-
-Now in order to use our navigation menu within a component we need to query it via GraphQL.
-
-```js
-// navigation.js
-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';
-
-class HeaderComponent extends LitElement {
-
- constructor() {
- super();
- this.navigation = [];
- }
-
- async connectedCallback() {
- super.connectedCallback();
-
- const response = await client.query({
- query: MenuQuery,
- variables: {
- name: 'navigation'
- }
- });
-
- this.navigation = response.data.menu.children.map(item => item.item);
- }
-
- render() {
- const { navigation } = this;
-
- return html`
-
-