Skip to content

Commit

Permalink
💽 Add site options to each page (#1536)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanc1 authored Sep 19, 2024
1 parent a2519bb commit 760e411
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .changeset/sour-apes-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"myst-frontmatter": patch
"myst-cli": patch
---

Enable site options on each page
13 changes: 13 additions & 0 deletions docs/website-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ Below is a table of options for each theme bundled with MyST.
:heading-depth: 3
```

### Page Options

Depending on the option, these can also be controlled in the frontmatter on each page under the `site` key.

```{code-block} yaml
:filename: my-page.md
---
...
site:
hide_toc: true
---
```

## Other top-level site configuration

There are some other top-level site configuration options not documented here.
Expand Down
4 changes: 2 additions & 2 deletions packages/myst-cli/src/build/html/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { SiteManifestOptions } from '../site/manifest.js';
import { getSiteManifest } from '../site/manifest.js';
import type { StartOptions } from '../site/start.js';
import { startServer } from '../site/start.js';
import { getMystTemplate } from '../site/template.js';
import { getSiteTemplate } from '../site/template.js';

export async function currentSiteRoutes(
session: ISession,
Expand Down Expand Up @@ -125,7 +125,7 @@ function get_baseurl(session: ISession): string | undefined {
* @param opts configuration options
*/
export async function buildHtml(session: ISession, opts: StartOptions) {
const template = await getMystTemplate(session, opts);
const template = await getSiteTemplate(session, opts);
// The BASE_URL env variable allows for mounting the site in a folder, e.g., github pages
const baseurl = get_baseurl(session);
// Note, this process is really only for Remix templates
Expand Down
4 changes: 2 additions & 2 deletions packages/myst-cli/src/build/site/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { selectors } from '../../store/index.js';
import { transformBanner, transformThumbnail } from '../../transforms/images.js';
import { addWarningForFile } from '../../utils/addWarningForFile.js';
import version from '../../version.js';
import { getMystTemplate } from './template.js';
import { getSiteTemplate } from './template.js';
import { collectExportOptions } from '../utils/collectExportOptions.js';
import { filterPages } from '../../project/load.js';
import { getRawFrontmatterFromFile } from '../../process/file.js';
Expand Down Expand Up @@ -391,7 +391,7 @@ export async function getSiteManifest(
?.map((action) => resolveSiteAction(session, action, siteConfigFile, 'actions'))
.filter((action): action is SiteAction => !!action);
const siteFrontmatter = filterKeys(siteConfig as Record<string, any>, SITE_FRONTMATTER_KEYS);
const mystTemplate = await getMystTemplate(session, opts);
const mystTemplate = await getSiteTemplate(session, opts);
const validatedOptions = mystTemplate.validateOptions(
siteFrontmatter.options ?? {},
siteConfigFile,
Expand Down
4 changes: 2 additions & 2 deletions packages/myst-cli/src/build/site/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { ISession } from '../../session/types.js';
import version from '../../version.js';
import { createServerLogger } from './logger.js';
import { buildSite } from './prepare.js';
import { installSiteTemplate, getMystTemplate } from './template.js';
import { installSiteTemplate, getSiteTemplate } from './template.js';
import { watchContent } from './watch.js';

const DEFAULT_START_COMMAND = 'npm run start';
Expand Down Expand Up @@ -122,7 +122,7 @@ export async function startServer(
// Ensure we are on the latest version of the configs
await session.reload();
warnOnHostEnvironmentVariable(session, opts);
const mystTemplate = await getMystTemplate(session, opts);
const mystTemplate = await getSiteTemplate(session, opts);
if (!opts.headless) await installSiteTemplate(session, mystTemplate);
await buildSite(session, opts);
const server = await startContentServer(session, opts);
Expand Down
8 changes: 6 additions & 2 deletions packages/myst-cli/src/build/site/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import MystTemplate from 'myst-templates';
import type { ISession } from '../../session/types.js';
import { selectors } from '../../store/index.js';
import { addWarningForFile } from '../../utils/addWarningForFile.js';
import { castSession } from '../../session/cache.js';

const DEFAULT_TEMPLATE = 'book-theme';
const DEFAULT_INSTALL_COMMAND = 'npm install';

export async function getMystTemplate(session: ISession, opts?: { defaultTemplate?: string }) {
const state = session.store.getState();
export async function getSiteTemplate(session: ISession, opts?: { defaultTemplate?: string }) {
const cache = castSession(session);
const state = cache.store.getState();
if (cache.$siteTemplate) return cache.$siteTemplate;
const siteConfig = selectors.selectCurrentSiteConfig(state);
const file = selectors.selectCurrentSiteFile(state) ?? session.configFiles[0];
const mystTemplate = new MystTemplate(session, {
Expand All @@ -30,6 +33,7 @@ export async function getMystTemplate(session: ISession, opts?: { defaultTemplat
},
});
await mystTemplate.ensureTemplateExistsOnPath();
cache.$siteTemplate = mystTemplate;
return mystTemplate;
}

Expand Down
17 changes: 17 additions & 0 deletions packages/myst-cli/src/frontmatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { VFile } from 'vfile';
import type { ISession } from './session/types.js';
import { selectors, watch } from './store/index.js';
import { logMessagesFromVFile } from './utils/logging.js';
import { castSession } from './session/cache.js';
import type { FileOptions } from 'myst-templates';

export function frontmatterValidationOpts(
vfile: VFile,
Expand Down Expand Up @@ -71,11 +73,26 @@ export function processPageFrontmatter(
validationOpts: ValidationOptions,
path?: string,
) {
const cache = castSession(session);
const state = session.store.getState();
const siteFrontmatter = selectors.selectCurrentSiteConfig(state) ?? {};
const projectFrontmatter = path ? selectors.selectLocalProjectConfig(state, path) ?? {} : {};

const frontmatter = fillPageFrontmatter(pageFrontmatter, projectFrontmatter, validationOpts);
const siteTemplate = cache.$siteTemplate;
if (siteTemplate) {
const siteOptions = siteTemplate.validateOptions(pageFrontmatter.site ?? {}, path, {
// The property is different on the page vs the myst.yml
property: 'site',
// Passing in the log files ensures this isn't prefixed with `myst.yml`.
warningLogFn: session.log.warn,
errorLogFn: session.log.error,
} as ValidationOptions & FileOptions);
if (siteOptions && Object.keys(siteOptions).length > 0) frontmatter.site = siteOptions;
} else {
// The options are still there, they are just not validated
session.log.debug(`Site template not available to validate site frontmatter in ${path}`);
}

if (siteFrontmatter?.options?.hide_authors || siteFrontmatter?.options?.design?.hide_authors) {
delete frontmatter.authors;
Expand Down
2 changes: 2 additions & 0 deletions packages/myst-cli/src/session/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Limit } from 'p-limit';
import type { BuildWarning, RootState } from '../store/index.js';
import type { PreRendererData, RendererData, SingleCitationRenderer } from '../transforms/types.js';
import type { SessionManager } from '@jupyterlab/services';
import type MystTemplate from 'myst-templates';

export type ISession = {
API_URL: string;
Expand Down Expand Up @@ -38,6 +39,7 @@ export type ISessionWithCache = ISession & {
$internalReferences: Record<string, ReferenceState>; // keyed on path
$externalReferences: Record<string, ResolvedExternalReference>; // keyed on id
$mdast: Record<string, { sha256?: string; pre: PreRendererData; post?: RendererData }>; // keyed on path
$siteTemplate: MystTemplate;
$outputs: MinifiedContentCache;
/** Method to get $mdast value with normalized path */
$getMdast(
Expand Down
3 changes: 3 additions & 0 deletions packages/myst-frontmatter/src/page/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const PAGE_FRONTMATTER_KEYS = [
'jupytext',
'tags',
'content_includes_title',
'site',
];

export type PageFrontmatter = ProjectAndPageFrontmatter & {
Expand All @@ -23,4 +24,6 @@ export type PageFrontmatter = ProjectAndPageFrontmatter & {
* Set during initial file/frontmatter load
*/
content_includes_title?: boolean;
/** Site Options, for example for turning off the outline on a single page */
site?: Record<string, any>;
};
6 changes: 6 additions & 0 deletions packages/myst-frontmatter/src/page/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
validateObjectKeys,
validateString,
validateBoolean,
validateObject,
} from 'simple-validators';
import { validateProjectAndPageFrontmatterKeys } from '../project/validators.js';
import { PAGE_FRONTMATTER_KEYS, type PageFrontmatter } from './types.js';
Expand Down Expand Up @@ -51,6 +52,11 @@ export function validatePageFrontmatterKeys(value: Record<string, any>, opts: Va
incrementOptions('content_includes_title', opts),
);
}
if (defined(value.site)) {
// These are validated later based on the siteTemplate
// At this point, they just need to be an object
output.site = validateObject(value.site, incrementOptions('site', opts));
}
return output;
}

Expand Down

0 comments on commit 760e411

Please sign in to comment.