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

📄 Enable sites with single projects and no project slug #374

Merged
merged 4 commits into from
May 10, 2023
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/brave-worms-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'myst-cli': patch
'myst-transforms': patch
---

Link/xref url respects index url, dataUrl is also provided for accessing mdast
6 changes: 6 additions & 0 deletions .changeset/cold-rockets-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'myst-cli': patch
'myst-config': patch
---

Enable single project site with no project slug
5 changes: 5 additions & 0 deletions .changeset/sixty-islands-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-cli': patch
---

Default to article theme for site builds with no explicit projects
2 changes: 1 addition & 1 deletion packages/myst-cli/src/build/site/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { getMystTemplate } from './template';
export async function localToManifestProject(
session: ISession,
projectPath: string,
projectSlug: string,
projectSlug?: string,
) {
const state = session.store.getState();
const projConfig = selectors.selectLocalProjectConfig(state, projectPath);
Expand Down
16 changes: 14 additions & 2 deletions packages/myst-cli/src/build/site/template.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import fs from 'fs';
import { join } from 'path';
import type { SiteConfig } from 'myst-config';
import { TemplateKind } from 'myst-common';
import { createNpmLogger, makeExecutable, tic } from 'myst-cli-utils';
import MystTemplate from 'myst-templates';
import type { ISession } from '../../session/types';
import { selectors } from '../../store';

const DEFAULT_SITE_TEMPLATE = 'book-theme';
const DEFAULT_SINGLE_PROJ_TEMPLATE = 'article-theme';
const DEFAULT_MULTI_PROJ_TEMPLATE = 'book-theme';
const DEFAULT_INSTALL_COMMAND = 'npm install';

function isSingleProject(siteConfig?: SiteConfig): boolean {
if (!siteConfig?.projects || (siteConfig.projects.length === 1 && !siteConfig.projects[0].slug)) {
return true;
}
return false;
}

export async function getMystTemplate(session: ISession, opts?: { defaultTemplate?: string }) {
const siteConfig = selectors.selectCurrentSiteConfig(session.store.getState());
const defaultTemplate = isSingleProject(siteConfig)
? DEFAULT_SINGLE_PROJ_TEMPLATE
: DEFAULT_MULTI_PROJ_TEMPLATE;
const mystTemplate = new MystTemplate(session, {
kind: TemplateKind.site,
template: siteConfig?.template ?? opts?.defaultTemplate ?? DEFAULT_SITE_TEMPLATE,
template: siteConfig?.template ?? opts?.defaultTemplate ?? defaultTemplate,
buildDir: session.buildPath(),
});
await mystTemplate.ensureTemplateExistsOnPath();
Expand Down
1 change: 1 addition & 0 deletions packages/myst-cli/src/build/utils/getFileContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export async function getFileContent(
projectPath,
pageSlug,
minifyMaxCharacters: 0,
index: project.index,
});
}),
);
Expand Down
11 changes: 10 additions & 1 deletion packages/myst-cli/src/process/mdast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export type PageReferenceStates = {
state: ReferenceState;
file: string;
url: string | null;
dataUrl: string | null;
}[];

export type TransformFn = (
Expand All @@ -88,6 +89,7 @@ export async function transformMdast(
watchMode?: boolean;
extraTransforms?: TransformFn[];
minifyMaxCharacters?: number;
index?: string;
},
) {
const {
Expand All @@ -101,6 +103,7 @@ export async function transformMdast(
extraTransforms,
watchMode = false,
minifyMaxCharacters,
index,
} = opts;
const toc = tic();
const { store, log } = session;
Expand Down Expand Up @@ -181,6 +184,11 @@ export async function transformMdast(
altOutputFolder: imageAltOutputFolder,
});
const sha256 = selectors.selectFileInfo(store.getState(), file).sha256 as string;
const useSlug = pageSlug !== index;
const url = projectSlug
? `/${projectSlug}/${useSlug ? pageSlug : ''}`
: `/${useSlug ? pageSlug : ''}`;
const dataUrl = projectSlug ? `/${projectSlug}/${pageSlug}.json` : `/${pageSlug}.json`;
store.dispatch(
watch.actions.updateFileInfo({
path: file,
Expand All @@ -191,7 +199,8 @@ export async function transformMdast(
thumbnail: frontmatter.thumbnail,
thumbnailOptimized: frontmatter.thumbnailOptimized,
tags: frontmatter.tags,
url: `/${projectSlug}/${pageSlug}`,
url,
dataUrl,
}),
);
const data: RendererData = {
Expand Down
54 changes: 29 additions & 25 deletions packages/myst-cli/src/process/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,15 @@ export async function loadProject(
export function selectPageReferenceStates(session: ISession, pages: { file: string }[]) {
const cache = castSession(session);
const pageReferenceStates: PageReferenceStates = pages
.map((page) => ({
state: cache.$internalReferences[page.file],
file: page.file,
url: selectors.selectFileInfo(session.store.getState(), page.file)?.url ?? null,
}))
.map((page) => {
const selectedFile = selectors.selectFileInfo(session.store.getState(), page.file);
return {
state: cache.$internalReferences[page.file],
file: page.file,
url: selectedFile?.url ?? null,
dataUrl: selectedFile?.dataUrl ?? null,
};
})
.filter(({ state }) => !!state);
return pageReferenceStates;
}
Expand Down Expand Up @@ -169,7 +173,7 @@ async function resolvePageExports(session: ISession, file: string) {

export async function writeFile(
session: ISession,
{ file, pageSlug, projectSlug }: { file: string; projectSlug: string; pageSlug: string },
{ file, pageSlug, projectSlug }: { file: string; pageSlug: string; projectSlug?: string },
) {
const toc = tic();
const selectedFile = selectFile(session, file);
Expand All @@ -180,9 +184,11 @@ export async function writeFile(
...(await resolvePageExports(session, file)),
]);
const frontmatterWithExports = { ...frontmatter, exports };
const jsonFilename = join(session.contentPath(), projectSlug, `${pageSlug}.json`);
const jsonFilenameParts = [session.contentPath()];
if (projectSlug) jsonFilenameParts.push(projectSlug);
jsonFilenameParts.push(`${pageSlug}.json`);
writeFileToFolder(
jsonFilename,
join(...jsonFilenameParts),
JSON.stringify({
kind,
sha256,
Expand All @@ -208,16 +214,17 @@ export async function fastProcessFile(
defaultTemplate,
}: {
file: string;
projectPath: string;
projectSlug: string;
pageSlug: string;
projectPath: string;
projectSlug?: string;
extraLinkTransformers?: LinkTransformer[];
extraTransforms?: TransformFn[];
defaultTemplate?: string;
},
) {
const toc = tic();
await loadFile(session, file);
const { project, pages } = await loadProject(session, projectPath);
await transformMdast(session, {
file,
imageWriteFolder: session.publicPath(),
Expand All @@ -228,8 +235,8 @@ export async function fastProcessFile(
pageSlug,
watchMode: true,
extraTransforms: [transformWebp, ...(extraTransforms ?? [])],
index: project.index,
});
const { pages } = await loadProject(session, projectPath);
const pageReferenceStates = selectPageReferenceStates(session, pages);
await postProcessMdast(session, {
file,
Expand Down Expand Up @@ -303,6 +310,7 @@ export async function processProject(
pageSlug: page.slug,
watchMode,
extraTransforms,
index: project.index,
}),
),
);
Expand All @@ -320,19 +328,15 @@ export async function processProject(
);
// Write all pages
if (writeFiles) {
if (siteProject.slug) {
await Promise.all(
pages.map((page) =>
writeFile(session, {
file: page.file,
projectSlug: siteProject.slug as string,
pageSlug: page.slug,
}),
),
);
} else {
log.error(`Cannot write project files without project slug`);
}
await Promise.all(
pages.map((page) =>
writeFile(session, {
file: page.file,
projectSlug: siteProject.slug as string,
pageSlug: page.slug,
}),
),
);
}
log.info(toc(`📚 Built ${pages.length} pages for ${siteProject.slug ?? 'project'} in %s.`));
return project;
Expand Down Expand Up @@ -394,7 +398,7 @@ export async function processSite(session: ISession, opts?: ProcessOptions): Pro
await writeSiteManifest(session, opts);
// Write the objects.inv
const inv = new Inventory({
project: siteConfig.title,
project: siteConfig?.title,
// TODO: allow a version on the project?!
version: String((siteConfig as any)?.version ?? '1'),
});
Expand Down
4 changes: 4 additions & 0 deletions packages/myst-cli/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type WatchedFile = {
tags?: string[] | null;
sha256?: string | null;
url?: string | null;
dataUrl?: string | null;
};

export const watch = createSlice({
Expand Down Expand Up @@ -126,6 +127,7 @@ export const watch = createSlice({
tags?: string[] | null;
sha256?: string;
url?: string;
dataUrl?: string;
}>,
) {
const {
Expand All @@ -139,6 +141,7 @@ export const watch = createSlice({
thumbnailOptimized,
tags,
url,
dataUrl,
} = action.payload;
const resolvedPath = resolve(path);
if (title) state.files[resolvedPath].title = title;
Expand All @@ -150,6 +153,7 @@ export const watch = createSlice({
if (tags) state.files[resolvedPath].tags = [...tags];
if (sha256) state.files[resolvedPath].sha256 = sha256;
if (url) state.files[resolvedPath].url = url;
if (dataUrl) state.files[resolvedPath].dataUrl = dataUrl;
},
},
});
Expand Down
10 changes: 8 additions & 2 deletions packages/myst-cli/src/store/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { resolve } from 'path';
import type { SiteConfig } from 'myst-config';
import type { LocalProject, LocalProjectPage } from '../project/types';
import type { RootState } from './reducers';
import type { BuildWarning, ExternalLinkResult } from './types';
Expand All @@ -15,9 +16,12 @@ export function selectLocalSiteConfig(state: RootState, path: string) {
return state.local.config.sites[resolve(path)];
}

export function selectCurrentSiteConfig(state: RootState) {
export function selectCurrentSiteConfig(state: RootState): SiteConfig | undefined {
if (!state.local.config.currentSitePath) return undefined;
return state.local.config.sites[resolve(state.local.config.currentSitePath)];
const config = state.local.config.sites[resolve(state.local.config.currentSitePath)];
const path = selectCurrentProjectPath(state);
if (config.projects || !path) return config;
return { ...config, projects: [{ path }] };
}

export function selectCurrentSiteTemplateOptions(state: RootState) {
Expand Down Expand Up @@ -78,6 +82,7 @@ export function selectFileInfo(state: RootState, path: string) {
tags,
sha256,
url,
dataUrl,
} = state.local.watch.files[resolve(path)] ?? {};
return {
title,
Expand All @@ -89,6 +94,7 @@ export function selectFileInfo(state: RootState, path: string) {
tags,
sha256,
url,
dataUrl,
};
}

Expand Down
4 changes: 3 additions & 1 deletion packages/myst-cli/src/transforms/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,14 @@ export class StaticFileTransformer implements LinkTransformer {
return false;
}
const [linkFile, ...target] = linkFileWithTarget.split('#');
const { url, title } = selectors.selectFileInfo(this.session.store.getState(), linkFile) || {};
const { url, title, dataUrl } =
selectors.selectFileInfo(this.session.store.getState(), linkFile) || {};
if (url != null) {
// Replace relative file link with resolved site path
// TODO: lookup the and resolve the hash as well
link.url = [url, ...(target || [])].join('#');
link.internal = true;
if (dataUrl) link.dataUrl = dataUrl;
} else {
// Copy relative file to static folder and replace with absolute link
const copiedFile = hashAndCopyStaticFile(this.session, linkFile, this.session.publicPath());
Expand Down
4 changes: 2 additions & 2 deletions packages/myst-config/src/site/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Export, ProjectFrontmatter, SiteFrontmatter } from 'myst-frontmatt
import { SITE_FRONTMATTER_KEYS } from 'myst-frontmatter';

export interface SiteProject {
slug: string;
slug?: string;
remote?: string;
path?: string;
}
Expand Down Expand Up @@ -54,7 +54,7 @@ type ManifestProjectItem = {
};

type ManifestProject = {
slug: string;
slug?: string;
index: string;
title: string;
pages: ManifestProjectItem[];
Expand Down
3 changes: 3 additions & 0 deletions packages/myst-config/src/site/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ function validateUrlOrPath(input: any, opts: ValidationOptions) {
}

export function validateSiteProject(input: any, opts: ValidationOptions) {
// While slug is not required on the type, it is required when validating the config
// i.e. we may construct a SiteProject dynamically with no slug, but if the user
// is explicitly defining a SiteProject, they must provide a slug.
const value = validateObjectKeys(
input,
{ required: ['slug'], optional: ['remote', 'path'] },
Expand Down
Loading