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 d9ecff1
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 228 deletions.
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
});
};
74 changes: 74 additions & 0 deletions packages/loaders/src/loadSnapshotFileContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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 matches = url.match(/(.*)[!/]/);
const urlPath = matches?.length ? matches[1] : '';

const fsRootUrl = getFSRootUrl();
const filePath = path.join(process.cwd(), fsRootUrl, urlPath);

let fileExists = false;
try {
await fs.stat(filePath);
fileExists = true;
} catch {}
if (fileExists) {
let localPath = filePath;
if ((await fs.stat(filePath)).isDirectory()) {
localPath = path.posix.join(localPath, 'index');
}
const realPath = await fs.realpath(localPath);
console.log(localPath, realPath);
const source = await fs.readFile(realPath, 'utf-8');
return JSON.parse(source);
}
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
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
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 d9ecff1

Please sign in to comment.