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

SLB-188: Decap integration #117

Merged
merged 20 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
86c39ca
refactor(SLB-188): simplify source typing for easer zod schemas
pmelab Dec 14, 2023
fcd6aad
refactor(SLB-188): simplify test data and structure
pmelab Dec 13, 2023
594602f
feat(SLB-188): generic preview function
pmelab Dec 13, 2023
295e3fb
refactor(SLB-188): clean up page collection code
pmelab Dec 13, 2023
fcf24fe
refactor(SLB-188): fix broken test data
pmelab Dec 13, 2023
3fb4753
feat(SLB-188): expose getPages function
pmelab Dec 13, 2023
8be7624
fix: lock graphql version
pmelab Dec 13, 2023
8c81cac
chore(SLB-188): merge content changes to release
pmelab Dec 14, 2023
623c4ab
feat(SLB-188): separate decap pages from drupal pages
pmelab Dec 14, 2023
5f487cb
feat(SLB-188): source and render decap pages
pmelab Dec 14, 2023
049576a
feat(SLB-188): serve decap ui in /admin subfolder
pmelab Dec 14, 2023
3370e10
ci: clear the drupal cache when schema changes
pmelab Dec 14, 2023
eb7a781
chore(SLB-188): adapt schema test cases to changes
pmelab Dec 14, 2023
05449f3
test(SLB-188): add e2e test case for decap pages
pmelab Dec 14, 2023
a7b9c51
refactor(SLB-188): remove leftover debug log
pmelab Dec 19, 2023
c06a8cf
refactor(SLB-188): fix naming of decap page template
pmelab Dec 19, 2023
18a995f
refactor(SLB-188): separate drupal and decap previews
pmelab Dec 19, 2023
2fcdfa8
fix: upgrade @amazeelabs/gatsby-silverback-cloudinary
pmelab Dec 19, 2023
1a430ae
fix(SLB-188): add missing 'originalSrc' to decap images
pmelab Dec 19, 2023
6f4891b
fix: add missing output declarations for autoloader files
pmelab Dec 19, 2023
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
12 changes: 12 additions & 0 deletions apps/decap/src/collections/page.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { dirname, resolve } from 'path';
import { expect, test } from 'vitest';

import { getPages } from '..';

test('getPages', () => {
const dir = resolve(
dirname(new URL(import.meta.url).pathname),
'../../data/page',
);
expect(() => getPages(dir)).not.toThrow();
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { ImageSource, PreviewPageQuery, Url } from '@custom/schema';
import { SilverbackSource } from '@amazeelabs/gatsby-source-silverback';
import {
BlockMarkupSource,
BlockMediaSource,
DecapPageSource,
LocaleSource,
MediaImageSource,
PageSource,
} from '@custom/schema/source';
import { Page } from '@custom/ui/routes/Page';
import {
CmsCollection,
CmsField,
PreviewTemplateComponentProps,
} from 'netlify-cms-core';
import { z, ZodType, ZodTypeDef } from 'zod';
import fs from 'fs';
import type { CmsCollection, CmsField } from 'netlify-cms-core';
import yaml from 'yaml';
import { z } from 'zod';

import { PreviewFrame } from '../helpers/frame';
import { transformMarkdown } from '../helpers/markdown';
import { useQuery } from '../helpers/query';

// =============================================================================
// Decap CMS collection definition.
Expand Down Expand Up @@ -139,112 +134,87 @@ export const PageCollection: CmsCollection = {
// Transformation schema definitions.
// =============================================================================

const BlockMarkupSchema: ZodType<BlockMarkupSource, ZodTypeDef, unknown> = z
const BlockMarkupSchema = z
.object({
type: z.literal('text'),
text: transformMarkdown,
})
.transform(({ text }) => {
.transform(({ text }): BlockMarkupSource => {
return {
__typename: 'BlockMarkup',
markup: text,
};
});

const BlockMediaImageSchema: ZodType<BlockMediaSource, ZodTypeDef, unknown> = z
const BlockMediaImageSchema = z
.object({
type: z.literal('image'),
alt: z.string(),
image: z.string(),
caption: transformMarkdown,
})
.transform(({ image, alt, caption }) => {
.transform(({ image, alt, caption }): BlockMediaSource => {
return {
__typename: 'BlockMedia',
media: {
__typename: 'MediaImage',
source: image as ImageSource,
source: image,
alt,
},
caption: caption,
};
});

export const pageSchema: ZodType<PageSource, ZodTypeDef, unknown> = z
.object({
__typename: z.literal('Page').optional().default('Page'),
id: z.string(),
title: z.string(),
locale: z.string().transform((l) => l as LocaleSource),
path: z.string().transform((p) => p as Url),
hero: z.object({
__typename: z.literal('Hero').optional().default('Hero'),
headline: z.string(),
lead: z.string().optional(),
image: z
.string()
.optional()
.transform((s) => s as ImageSource)
.transform(
(source) =>
({
export const pageSchema = z.object({
__typename: z.literal('DecapPage').optional().default('DecapPage'),
id: z.string(),
title: z.string(),
locale: z.string().transform((l) => l as LocaleSource),
path: z.string(),
hero: z.object({
__typename: z.literal('Hero').optional().default('Hero'),
headline: z.string(),
lead: z.string().optional(),
image: z
.string()
.optional()
.transform((source): MediaImageSource | undefined =>
source
? {
__typename: 'MediaImage',
source,
alt: '',
} satisfies MediaImageSource),
),
}),
content: z.array(z.union([BlockMarkupSchema, BlockMediaImageSchema])),
})
.transform((page) => ({
...page,
id: `${page.id}:${page.locale}`,
}));

// =============================================================================
// Decap CMS preview component.
// =============================================================================
export function PagePreview({
entry,
getAsset,
}: PreviewTemplateComponentProps) {
// Extract data from Decap input.
const input = entry.toJS().data;

// Parse that input and transform it to a GraphQL Source input.
const parsed = pageSchema.safeParse({ ...input, locale: 'en' });
if (!parsed.success) {
console.error(parsed.error);
}

const previewSourceData = parsed.success
? parsed.data
: ({
__typename: 'Page',
title: '[Missing title]',
path: '/preview' as Url,
locale: 'en',
} satisfies PageSource);

// Execute the "Preview" query on that source input to transform
// data into the exact shape of the query result expected by the
// route.
const data = useQuery(
PreviewPageQuery,
{
previewPage: previewSourceData,
} satisfies PreviewPageQuery,
{
id: '',
rid: '',
locale: '',
},
(src) => getAsset(src).url,
);
}
: undefined,
),
}),
content: z.array(z.union([BlockMarkupSchema, BlockMediaImageSchema])),
});

return (
<PreviewFrame>
{data?.previewPage ? <Page page={data.previewPage} /> : null}
</PreviewFrame>
);
}
export const getPages: (dir: string) => SilverbackSource<DecapPageSource> =
(dir: string) => () => {
const pages: Array<[string, DecapPageSource]> = [];
fs.readdirSync(dir)
.filter((file) => file.endsWith('.yml'))
.forEach((file) => {
const content = yaml.parse(fs.readFileSync(`${dir}/${file}`, 'utf-8'));
Object.keys(content).forEach((lang) => {
if (Object.keys(content[lang]).length < 2) {
return;
}
const input = {
...content[lang],
locale: lang,
};
const page = pageSchema.safeParse(input);
if (page.success) {
pages.push([page.data.id, page.data]);
} else {
console.warn(`Error parsing ${file} (${lang}):`);
console.warn(page.error.message);
console.warn('Input:', content[lang]);
}
});
});
return pages;
};
18 changes: 8 additions & 10 deletions apps/decap/src/helpers/markdown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Markup } from '@custom/schema';
import rehypeSanitize from 'rehype-sanitize';
import rehypeStringify from 'rehype-stringify';
import remarkParse from 'remark-parse';
Copy link
Member

Choose a reason for hiding this comment

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

I had many issues updating these in silverback-mono. Just complaining 😅

Expand All @@ -9,13 +8,12 @@ import { z } from 'zod';
export const transformMarkdown = z
.string()
.optional()
.transform(
(t) =>
unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeSanitize)
.use(rehypeStringify)
.processSync(t)
.toString() as Markup,
.transform((t) =>
unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeSanitize)
.use(rehypeStringify)
.processSync(t)
.toString(),
);
43 changes: 1 addition & 42 deletions apps/decap/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1 @@
import { PageSource } from '@custom/schema/source';
import fs from 'fs';
import { dirname, resolve } from 'path';
import yaml from 'yaml';

import { pageSchema } from './collections/page';

export function getPages() {
const dir = resolve(dirname(new URL(import.meta.url).pathname), '../data');
const settings = yaml.parse(fs.readFileSync(`${dir}/site.yml`, 'utf-8')) as {
homePage: string;
notFoundPage: string;
};
const pages: Array<PageSource> = [];
fs.readdirSync(`${dir}/page`)
.filter((file) => file.endsWith('.yml'))
.forEach((file) => {
const content = yaml.parse(
fs.readFileSync(`${dir}/page/${file}`, 'utf-8'),
);
Object.keys(content).forEach((lang) => {
const input = {
...content[lang],
locale: lang,
path:
content[lang].path === settings.homePage
? '/'
: content[lang].path === settings.notFoundPage
? '/404'
: content[lang].path,
};
const page = pageSchema.safeParse(input);
if (page.success) {
pages.push(page.data);
} else {
console.warn(`Error parsing ${file} (${lang}):`);
console.warn(page.error.message);
}
});
});
return pages;
}
export { getPages } from './collections/page.js';