Skip to content

Commit

Permalink
Re-add support for snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
joshwooding committed Nov 7, 2024
1 parent 0540260 commit d2070d8
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 234 deletions.
1 change: 1 addition & 0 deletions packages/loaders/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dependencies": {
"@jpmorganchase/mosaic-store": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-schemas": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-types": "^0.1.0-beta.89",
"@types/node": "^18.15.3",
"deepmerge": "^4.2.2",
"gray-matter": "^4.0.3",
Expand Down
7 changes: 7 additions & 0 deletions packages/loaders/src/LoadPageError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class LoadPageError extends Error {
statusCode: number;
constructor({ message, statusCode }: { message: string; statusCode: number }) {
super(message);
this.statusCode = statusCode;
}
}
71 changes: 15 additions & 56 deletions packages/loaders/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,22 @@
import path from 'path';
import matter from 'gray-matter';
import type { SafeParseError } from 'zod';
import { activeEnvSchema } from '@jpmorganchase/mosaic-schemas';
import type { SharedConfig } from '@jpmorganchase/mosaic-store';
import type { MosaicMode } from '@jpmorganchase/mosaic-types';

import type { LoaderPage } from './types/index.js';
import { loadActiveContent, loadActiveMosaicData } from './loadActiveContent';
import { loadSnapshotFileContent, loadSnapshotFileMosaicData } from './loadSnapshotFileContent';

export { LoadPageError } from './LoadPageError';
export * from './types/index.js';

const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);

type ActiveModeUrlEnv = {
MOSAIC_ACTIVE_MODE_URL: string;
};

export class LoadPageError extends Error {
statusCode: number;
constructor({ message, statusCode }: { message: string; statusCode: number }) {
super(message);
this.statusCode = statusCode;
}
}

const getFSRootUrl = (): string => {
const env = activeEnvSchema.safeParse(process.env);
if (!env.success) {
const { error } = env as SafeParseError<ActiveModeUrlEnv>;
error.issues.forEach(issue => {
console.error(
`Missing process.env.${issue.path.join()} environment variable required to load pages`
);
});
throw new LoadPageError({
message: `Environment variables missing to load pages`,
statusCode: 500
});
}
return env.data.MOSAIC_ACTIVE_MODE_URL;
};

export const loadMosaicData = async <T>(url: string): Promise<T> => {
const fsRootUrl = getFSRootUrl();
const dataUrl = new URL(url, fsRootUrl);
const response = await fetch(dataUrl);
const mode: MosaicMode = (process.env.MOSAIC_MODE || 'active') as MosaicMode;

if (!response.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error(`Failed to fetch mosaic data @ ${dataUrl}`);
if (mode === 'snapshot-file') {
return loadSnapshotFileMosaicData(url);
}
return response.json();

return loadActiveMosaicData(url);
};

export const loadSharedConfig = async (route: string): Promise<SharedConfig | undefined> => {
Expand All @@ -58,20 +26,11 @@ export const loadSharedConfig = async (route: string): Promise<SharedConfig | un
};

export const loadPage = async (route: string): Promise<LoaderPage> => {
const fsRootUrl = getFSRootUrl();
const pageUrl = normalizePageUrl(`${fsRootUrl}${route}`);
const response = await fetch(pageUrl);
if (response.status === 302) {
const { redirect } = await response.json();
return loadPage(redirect);
}
if (response.ok) {
const source = await response.text();
const { content, data } = matter(source);
return { source: content, data };
const mode: MosaicMode = (process.env.MOSAIC_MODE || 'active') as MosaicMode;

if (mode === 'snapshot-file') {
return loadSnapshotFileContent(route);
}
throw new LoadPageError({
message: `Could not load page : ${pageUrl} ${response.status}/${response.statusText}`,
statusCode: 404
});

return loadActiveContent(route);
};
60 changes: 60 additions & 0 deletions packages/loaders/src/loadActiveContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import matter from 'gray-matter';
import type { SafeParseError } from 'zod';
import { activeEnvSchema } from '@jpmorganchase/mosaic-schemas';

import { LoadPageError } from './LoadPageError';
import type { LoaderPage } from './types/index.js';

const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);

type ActiveModeUrlEnv = {
MOSAIC_ACTIVE_MODE_URL: string;
};

const getFSRootUrl = (): string => {
const env = activeEnvSchema.safeParse(process.env);
if (!env.success) {
const { error } = env as SafeParseError<ActiveModeUrlEnv>;
error.issues.forEach(issue => {
console.error(
`Missing process.env.${issue.path.join()} environment variable required to load pages`
);
});
throw new LoadPageError({
message: `Environment variables missing to load pages`,
statusCode: 500
});
}
return env.data.MOSAIC_ACTIVE_MODE_URL;
};

export const loadActiveMosaicData = async <T>(url: string): Promise<T> => {
const fsRootUrl = getFSRootUrl();
const dataUrl = new URL(url, fsRootUrl);
const response = await fetch(dataUrl);

if (!response.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error(`Failed to fetch mosaic data @ ${dataUrl}`);
}
return response.json();
};

export const loadActiveContent = async (route: string): Promise<LoaderPage> => {
const fsRootUrl = getFSRootUrl();
const pageUrl = normalizePageUrl(`${fsRootUrl}${route}`);
const response = await fetch(pageUrl);
if (response.status === 302) {
const { redirect } = await response.json();
return loadActiveContent(redirect);
}
if (response.ok) {
const source = await response.text();
const { content, data } = matter(source);
return { source: content, data };
}
throw new LoadPageError({
message: `Could not load page : ${pageUrl} ${response.status}/${response.statusText}`,
statusCode: 404
});
};
62 changes: 62 additions & 0 deletions packages/loaders/src/loadSnapshotFileContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import path from 'path';
import fs from 'node:fs/promises';
import matter from 'gray-matter';
import { snapshotFileEnvSchema } from '@jpmorganchase/mosaic-schemas';

import { LoadPageError } from './LoadPageError';
import type { LoaderPage } from './types/index.js';

const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);

const getFSRootUrl = (): string => {
const env = snapshotFileEnvSchema.safeParse(process.env);
if (!env.success) {
env.error.issues.forEach(issue => {
console.error(
`Missing process.env.${issue.path.join()} environment variable required to load pages`
);
});
throw new LoadPageError({
message: `Environment variables missing to load pages`,
statusCode: 500
});
}
return env.data.MOSAIC_SNAPSHOT_DIR;
};

export const loadSnapshotFileMosaicData = async <T>(url: string): Promise<T> => {
const fsRootUrl = getFSRootUrl();
const filePath = path.join(process.cwd(), fsRootUrl, url);
try {
const realPath = await fs.realpath(filePath);

const source = await fs.readFile(realPath, 'utf-8');
return JSON.parse(source);
} catch {
throw new Error(`Failed to fetch mosaic data @ ${url}`);
}
};

export const loadSnapshotFileContent = async (route: string): Promise<LoaderPage> => {
const fsRootUrl = getFSRootUrl();
const pageUrl = normalizePageUrl(route);
const filePath = path.posix.join(process.cwd(), fsRootUrl, pageUrl);
try {
let localPath = filePath;
if ((await fs.stat(filePath)).isDirectory()) {
localPath = path.posix.join(localPath, 'index');
}
const realPath = await fs.realpath(localPath);
const source = await fs.readFile(realPath, 'utf-8');
const { content, data } = matter(source);
return { source: content, data };
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
}
throw new LoadPageError({
message: `Could not read local file '${filePath}' for '${route}'`,
statusCode: 404
});
}
};
1 change: 0 additions & 1 deletion packages/site-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"@jpmorganchase/mosaic-content-editor-plugin": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-labs-components": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-open-api-component": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-site-middleware": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-store": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-theme": "^0.1.0-beta.89",
"@salt-ds/core": "^1.33.0",
Expand Down
12 changes: 8 additions & 4 deletions packages/site-components/src/Drawer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import classNames from 'clsx';
import { LayerLayout } from '@salt-ds/lab';
Expand Down Expand Up @@ -27,14 +27,18 @@ export function Drawer({ children, TriggerElement, side }: DrawerProps) {
const handleNavigationToggle = () => {
setOpen(!open);
};
const [portalElement, setPortalElement] = useState<HTMLElement | null>(null);

useEffect(() => {
setPortalElement(document.querySelector<HTMLElement>('[data-mosaic-id="portal-root"]'));
}, []);

const portalRoot = document.querySelector('[data-mosaic-id="portal-root"]');
return (
<>
<div ref={triggerRef}>
<TriggerElement open={open} onClick={handleNavigationToggle} />
</div>
{portalRoot
{portalElement
? createPortal(
<div className={styles.root}>
<LayerLayout
Expand Down Expand Up @@ -62,7 +66,7 @@ export function Drawer({ children, TriggerElement, side }: DrawerProps) {
{children}
</LayerLayout>
</div>,
portalRoot
portalElement
)
: null}
</>
Expand Down
1 change: 1 addition & 0 deletions packages/site-preset-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@jpmorganchase/mosaic-site-components": "0.1.0-beta.89",
"@jpmorganchase/mosaic-layouts": "0.1.0-beta.89",
"@jpmorganchase/mosaic-theme": "0.1.0-beta.89",
"@jpmorganchase/mosaic-mdx-components": "0.1.0-beta.89",
"prismjs": "^1.23.0"
},
"peerDependencies": {}
Expand Down
2 changes: 0 additions & 2 deletions packages/site/next.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const webpack = require('webpack');

module.exports = {
reactStrictMode: true,
swcMinify: true,
Expand Down
1 change: 1 addition & 0 deletions packages/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@jpmorganchase/mosaic-layouts": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-loaders": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-site-components": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-site-components-next": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-site-preset-styles": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-sitemap-component": "^0.1.0-beta.89",
"@jpmorganchase/mosaic-source-git-repo": "^0.1.0-beta.89",
Expand Down
Loading

0 comments on commit d2070d8

Please sign in to comment.