Skip to content

Commit 4ef187d

Browse files
committed
Re-add support for snapshots
1 parent 0540260 commit 4ef187d

5 files changed

Lines changed: 168 additions & 228 deletions

File tree

packages/loaders/src/index.ts

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,21 @@
11
import path from 'path';
2-
import matter from 'gray-matter';
3-
import type { SafeParseError } from 'zod';
4-
import { activeEnvSchema } from '@jpmorganchase/mosaic-schemas';
52
import type { SharedConfig } from '@jpmorganchase/mosaic-store';
3+
import type { MosaicMode } from '@jpmorganchase/mosaic-types';
64

75
import type { LoaderPage } from './types/index.js';
6+
import { loadActiveContent, loadActiveMosaicData } from './loadActiveContent';
7+
import { loadSnapshotFileContent, loadSnapshotFileMosaicData } from './loadSnapshotFileContent';
88

99
export * from './types/index.js';
1010

11-
const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);
12-
13-
type ActiveModeUrlEnv = {
14-
MOSAIC_ACTIVE_MODE_URL: string;
15-
};
16-
17-
export class LoadPageError extends Error {
18-
statusCode: number;
19-
constructor({ message, statusCode }: { message: string; statusCode: number }) {
20-
super(message);
21-
this.statusCode = statusCode;
22-
}
23-
}
24-
25-
const getFSRootUrl = (): string => {
26-
const env = activeEnvSchema.safeParse(process.env);
27-
if (!env.success) {
28-
const { error } = env as SafeParseError<ActiveModeUrlEnv>;
29-
error.issues.forEach(issue => {
30-
console.error(
31-
`Missing process.env.${issue.path.join()} environment variable required to load pages`
32-
);
33-
});
34-
throw new LoadPageError({
35-
message: `Environment variables missing to load pages`,
36-
statusCode: 500
37-
});
38-
}
39-
return env.data.MOSAIC_ACTIVE_MODE_URL;
40-
};
41-
4211
export const loadMosaicData = async <T>(url: string): Promise<T> => {
43-
const fsRootUrl = getFSRootUrl();
44-
const dataUrl = new URL(url, fsRootUrl);
45-
const response = await fetch(dataUrl);
12+
const mode: MosaicMode = (process.env.MOSAIC_MODE || 'active') as MosaicMode;
4613

47-
if (!response.ok) {
48-
// This will activate the closest `error.js` Error Boundary
49-
throw new Error(`Failed to fetch mosaic data @ ${dataUrl}`);
14+
if (mode === 'snapshot-file') {
15+
return loadSnapshotFileMosaicData(url);
5016
}
51-
return response.json();
17+
18+
return loadActiveMosaicData(url);
5219
};
5320

5421
export const loadSharedConfig = async (route: string): Promise<SharedConfig | undefined> => {
@@ -58,20 +25,11 @@ export const loadSharedConfig = async (route: string): Promise<SharedConfig | un
5825
};
5926

6027
export const loadPage = async (route: string): Promise<LoaderPage> => {
61-
const fsRootUrl = getFSRootUrl();
62-
const pageUrl = normalizePageUrl(`${fsRootUrl}${route}`);
63-
const response = await fetch(pageUrl);
64-
if (response.status === 302) {
65-
const { redirect } = await response.json();
66-
return loadPage(redirect);
67-
}
68-
if (response.ok) {
69-
const source = await response.text();
70-
const { content, data } = matter(source);
71-
return { source: content, data };
28+
const mode: MosaicMode = (process.env.MOSAIC_MODE || 'active') as MosaicMode;
29+
30+
if (mode === 'snapshot-file') {
31+
return loadSnapshotFileContent(route);
7232
}
73-
throw new LoadPageError({
74-
message: `Could not load page : ${pageUrl} ${response.status}/${response.statusText}`,
75-
statusCode: 404
76-
});
33+
34+
return loadActiveContent(route);
7735
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import matter from 'gray-matter';
2+
import type { SafeParseError } from 'zod';
3+
import { activeEnvSchema } from '@jpmorganchase/mosaic-schemas';
4+
5+
import type { LoaderPage } from './types/index.js';
6+
7+
export * from './types/index.js';
8+
9+
const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);
10+
11+
type ActiveModeUrlEnv = {
12+
MOSAIC_ACTIVE_MODE_URL: string;
13+
};
14+
15+
export class LoadPageError extends Error {
16+
statusCode: number;
17+
constructor({ message, statusCode }: { message: string; statusCode: number }) {
18+
super(message);
19+
this.statusCode = statusCode;
20+
}
21+
}
22+
23+
const getFSRootUrl = (): string => {
24+
const env = activeEnvSchema.safeParse(process.env);
25+
if (!env.success) {
26+
const { error } = env as SafeParseError<ActiveModeUrlEnv>;
27+
error.issues.forEach(issue => {
28+
console.error(
29+
`Missing process.env.${issue.path.join()} environment variable required to load pages`
30+
);
31+
});
32+
throw new LoadPageError({
33+
message: `Environment variables missing to load pages`,
34+
statusCode: 500
35+
});
36+
}
37+
return env.data.MOSAIC_ACTIVE_MODE_URL;
38+
};
39+
40+
export const loadActiveMosaicData = async <T>(url: string): Promise<T> => {
41+
const fsRootUrl = getFSRootUrl();
42+
const dataUrl = new URL(url, fsRootUrl);
43+
const response = await fetch(dataUrl);
44+
45+
if (!response.ok) {
46+
// This will activate the closest `error.js` Error Boundary
47+
throw new Error(`Failed to fetch mosaic data @ ${dataUrl}`);
48+
}
49+
return response.json();
50+
};
51+
52+
export const loadActiveContent = async (route: string): Promise<LoaderPage> => {
53+
const fsRootUrl = getFSRootUrl();
54+
const pageUrl = normalizePageUrl(`${fsRootUrl}${route}`);
55+
const response = await fetch(pageUrl);
56+
if (response.status === 302) {
57+
const { redirect } = await response.json();
58+
return loadActiveContent(redirect);
59+
}
60+
if (response.ok) {
61+
const source = await response.text();
62+
const { content, data } = matter(source);
63+
return { source: content, data };
64+
}
65+
throw new LoadPageError({
66+
message: `Could not load page : ${pageUrl} ${response.status}/${response.statusText}`,
67+
statusCode: 404
68+
});
69+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { snapshotFileEnvSchema } from '@jpmorganchase/mosaic-schemas';
2+
3+
import type { LoaderPage } from './types/index.js';
4+
import path from 'path';
5+
import fs from 'node:fs/promises';
6+
import matter from 'gray-matter';
7+
8+
export * from './types/index.js';
9+
10+
const normalizePageUrl = (url: string): string => (/\/index$/.test(url) ? `${url}.mdx` : url);
11+
12+
export class LoadPageError extends Error {
13+
statusCode: number;
14+
constructor({ message, statusCode }: { message: string; statusCode: number }) {
15+
super(message);
16+
this.statusCode = statusCode;
17+
}
18+
}
19+
20+
const getFSRootUrl = (): string => {
21+
const env = snapshotFileEnvSchema.safeParse(process.env);
22+
if (!env.success) {
23+
env.error.issues.forEach(issue => {
24+
console.error(
25+
`Missing process.env.${issue.path.join()} environment variable required to load pages`
26+
);
27+
});
28+
throw new LoadPageError({
29+
message: `Environment variables missing to load pages`,
30+
statusCode: 500
31+
});
32+
}
33+
return env.data.MOSAIC_SNAPSHOT_DIR;
34+
};
35+
36+
export const loadSnapshotFileMosaicData = async <T>(url: string): Promise<T> => {
37+
const matches = url.match(/(.*)[!/]/);
38+
const urlPath = matches?.length ? matches[1] : '';
39+
40+
const fsRootUrl = getFSRootUrl();
41+
const filePath = path.join(process.cwd(), fsRootUrl, urlPath);
42+
43+
let fileExists = false;
44+
try {
45+
await fs.stat(filePath);
46+
fileExists = true;
47+
} catch {}
48+
if (fileExists) {
49+
let localPath = filePath;
50+
if ((await fs.stat(filePath)).isDirectory()) {
51+
localPath = path.posix.join(localPath, 'index');
52+
}
53+
const realPath = await fs.realpath(localPath);
54+
console.log(localPath, realPath);
55+
const source = await fs.readFile(realPath, 'utf-8');
56+
return JSON.parse(source);
57+
}
58+
throw new Error(`Failed to fetch mosaic data @ ${url}`);
59+
};
60+
61+
export const loadSnapshotFileContent = async (route: string): Promise<LoaderPage> => {
62+
const fsRootUrl = getFSRootUrl();
63+
const pageUrl = normalizePageUrl(route);
64+
const filePath = path.posix.join(process.cwd(), fsRootUrl, pageUrl);
65+
try {
66+
let localPath = filePath;
67+
if ((await fs.stat(filePath)).isDirectory()) {
68+
localPath = path.posix.join(localPath, 'index');
69+
}
70+
const realPath = await fs.realpath(localPath);
71+
const source = await fs.readFile(realPath, 'utf-8');
72+
const { content, data } = matter(source);
73+
return { source: content, data };
74+
} catch (error) {
75+
if (error instanceof Error) {
76+
console.error(error.message);
77+
}
78+
throw new LoadPageError({
79+
message: `Could not read local file '${filePath}' for '${route}'`,
80+
statusCode: 404
81+
});
82+
}
83+
};

packages/site-components/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
"@jpmorganchase/mosaic-content-editor-plugin": "^0.1.0-beta.89",
4545
"@jpmorganchase/mosaic-labs-components": "^0.1.0-beta.89",
4646
"@jpmorganchase/mosaic-open-api-component": "^0.1.0-beta.89",
47-
"@jpmorganchase/mosaic-site-middleware": "^0.1.0-beta.89",
4847
"@jpmorganchase/mosaic-store": "^0.1.0-beta.89",
4948
"@jpmorganchase/mosaic-theme": "^0.1.0-beta.89",
5049
"@salt-ds/core": "^1.33.0",

0 commit comments

Comments
 (0)