Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💽 Add site options to each page #1536

Merged
merged 2 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
fwkoch marked this conversation as resolved.
Show resolved Hide resolved
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));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this overlap with options, which already exists per-page?

}
return output;
}

Expand Down
Loading