Refactored CMS packages to remove a circular dependency (#62)

This commit is contained in:
Giancarlo Buomprisco
2024-09-04 03:10:50 +08:00
committed by GitHub
parent 51a40b6d40
commit d18f810c6e
23 changed files with 193 additions and 83 deletions

View File

@@ -13,9 +13,12 @@
".": "./src/index.ts"
},
"devDependencies": {
"@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*",
"@kit/keystatic": "workspace:^",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/wordpress": "workspace:^",
"@types/node": "^22.5.2"
},
"eslintConfig": {

View File

@@ -1,4 +1,4 @@
import type { CmsType } from './cms.type';
import type { CmsType } from '@kit/cms-types';
const CMS_CLIENT = process.env.CMS_CLIENT as CmsType;
@@ -11,21 +11,32 @@ export async function ContentRenderer({
content,
type = CMS_CLIENT,
}: ContentRendererProps) {
const Renderer = await getContentRenderer(type);
return Renderer ? <Renderer content={content} /> : null;
}
/**
* Gets the content renderer for the specified CMS client.
*
* @param {CmsType} type - The type of CMS client.
*/
async function getContentRenderer(type: CmsType) {
switch (type) {
case 'keystatic': {
const { KeystaticContentRenderer } = await import(
'../../keystatic/src/content-renderer'
'@kit/keystatic/renderer'
);
return <KeystaticContentRenderer content={content} />;
return KeystaticContentRenderer;
}
case 'wordpress': {
const { WordpressContentRenderer } = await import(
'../../wordpress/src/content-renderer'
'@kit/wordpress/renderer'
);
return <WordpressContentRenderer content={content} />;
return WordpressContentRenderer;
}
default: {

View File

@@ -1,5 +1,9 @@
import { CmsClient } from './cms-client';
import { CmsType } from './cms.type';
import { CmsClient, CmsType } from '@kit/cms-types';
/**
* The type of CMS client to use.
*/
const CMS_CLIENT = process.env.CMS_CLIENT as CmsType;
/**
* Creates a CMS client based on the specified type.
@@ -9,61 +13,32 @@ import { CmsType } from './cms.type';
* @throws {Error} If the specified CMS type is unknown.
*/
export async function createCmsClient(
type: CmsType = process.env.CMS_CLIENT as CmsType,
type: CmsType = CMS_CLIENT,
): Promise<CmsClient> {
return cmsClientFactory(type);
}
async function cmsClientFactory(type: CmsType) {
/**
* Creates a CMS client based on the specified type.
*
* @param {CmsType} type - The type of CMS client to create.
* @returns {Promise<CmsClient>} A Promise that resolves to the created CMS client.
*/
async function cmsClientFactory(type: CmsType): Promise<CmsClient> {
switch (type) {
case 'wordpress':
return getWordpressClient();
case 'wordpress': {
const { createWordpressClient } = await import('@kit/wordpress');
case 'keystatic':
return getKeystaticClient();
return createWordpressClient();
}
case 'keystatic': {
const { createKeystaticClient } = await import('@kit/keystatic');
return createKeystaticClient();
}
default:
throw new Error(`Unknown CMS type`);
}
}
async function getWordpressClient() {
const { createWordpressClient } = await import(
'../../wordpress/src/wp-client'
);
return createWordpressClient();
}
async function getKeystaticClient() {
if (
process.env.NEXT_RUNTIME === 'nodejs' ||
process.env.KEYSTATIC_STORAGE_KIND !== 'local'
) {
const { createKeystaticClient } = await import(
'../../keystatic/src/keystatic-client'
);
return createKeystaticClient();
}
console.error(
`[CMS] Keystatic client using "Local" mode is only available in Node.js runtime. Please choose a different CMS client. Returning a mock client instead of throwing an error.`,
);
return mockCMSClient() as unknown as CmsClient;
}
function mockCMSClient() {
return {
getContentItems() {
return Promise.resolve({
items: [],
total: 0,
});
},
getContentItemBySlug() {
return Promise.resolve(undefined);
},
};
}

View File

@@ -1,4 +1,6 @@
export * from './cms-client';
import { Cms } from '@kit/cms-types';
export * from './create-cms-client';
export * from './cms.type';
export * from './content-renderer';
export type { Cms };

View File

@@ -11,6 +11,7 @@
"prettier": "@kit/prettier-config",
"exports": {
".": "./src/index.ts",
"./renderer": "./src/content-renderer.tsx",
"./admin": "./src/keystatic-admin.tsx",
"./route-handler": "./src/keystatic-route-handler.ts"
},
@@ -20,7 +21,7 @@
"@markdoc/markdoc": "^0.4.0"
},
"devDependencies": {
"@kit/cms": "workspace:^",
"@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",

View File

@@ -0,0 +1,37 @@
import { CmsClient } from '@kit/cms-types';
/**
* Creates a new Keystatic client instance.
*/
export async function createKeystaticClient() {
if (
process.env.NEXT_RUNTIME === 'nodejs' ||
process.env.KEYSTATIC_STORAGE_KIND !== 'local'
) {
const { createKeystaticClient: createClient } = await import(
'./keystatic-client'
);
return createClient();
}
console.error(
`[CMS] Keystatic client using "Local" mode is only available in Node.js runtime. Please choose a different CMS client. Returning a mock client instead of throwing an error.`,
);
return mockCMSClient() as unknown as CmsClient;
}
function mockCMSClient() {
return {
getContentItems() {
return Promise.resolve({
items: [],
total: 0,
});
},
getContentItemBySlug() {
return Promise.resolve(undefined);
},
};
}

View File

@@ -1,18 +1,22 @@
import { z } from 'zod';
import { keyStaticConfig } from './keystatic.config';
/**
* The kind of storage to use for the Keystatic reader.
*/
const STORAGE_KIND = process.env.KEYSTATIC_STORAGE_KIND ?? 'local';
/**
* Create a KeyStatic reader based on the storage kind.
* Creates a new Keystatic reader instance.
*/
export async function createKeystaticReader() {
switch (STORAGE_KIND) {
case 'local': {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { default: config } = await import('./keystatic.config');
const { createReader } = await import('@keystatic/core/reader');
return createReader(process.cwd(), config);
return createReader(process.cwd(), keyStaticConfig);
} else {
// we should never get here but the compiler requires the check
// to ensure we don't parse the package at build time
@@ -22,11 +26,11 @@ export async function createKeystaticReader() {
case 'github':
case 'cloud': {
const { default: config } = await import('./keystatic.config');
const githubConfig = z
.object({
token: z.string(),
token: z.string({
description: 'The GitHub token to use for authentication.',
}),
repo: z.custom<`${string}/${string}`>(),
pathPrefix: z.string().optional(),
})
@@ -40,7 +44,7 @@ export async function createKeystaticReader() {
'@keystatic/core/reader/github'
);
return createGitHubReader(config, githubConfig);
return createGitHubReader(keyStaticConfig, githubConfig);
}
default:

View File

@@ -1,3 +1 @@
export * from './keystatic-client';
export * from './content-renderer';
export * from './keystatic.config';
export * from './create-keystatic-cms';

View File

@@ -2,6 +2,6 @@
import { makePage } from '@keystatic/next/ui/app';
import config from './keystatic.config';
import { keyStaticConfig } from './keystatic.config';
export default makePage(config);
export default makePage(keyStaticConfig);

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Cms, CmsClient } from '@kit/cms';
import { Cms, CmsClient } from '@kit/cms-types';
import { createKeystaticReader } from './create-reader';
import {

View File

@@ -1,9 +1,9 @@
import { makeRouteHandler } from '@keystatic/next/route-handler';
import config from './keystatic.config';
import { keyStaticConfig } from './keystatic.config';
const handlers = makeRouteHandler({
config,
config: keyStaticConfig,
});
/**

View File

@@ -43,12 +43,10 @@ const storage = z.union([local, cloud, github]).parse({
pathPrefix: process.env.KEYSTATIC_PATH_PREFIX,
});
const keyStaticConfig = createKeyStaticConfig(
export const keyStaticConfig = createKeyStaticConfig(
process.env.NEXT_PUBLIC_KEYSTATIC_CONTENT_PATH ?? '',
);
export default keyStaticConfig;
function getContentField() {
return fields.markdoc({
label: 'Content',

View File

@@ -0,0 +1,3 @@
# CMS - @kit/cms
CMS abstraction layer for the Makerkit framework.

View File

@@ -0,0 +1,34 @@
{
"name": "@kit/cms-types",
"private": true,
"version": "0.1.0",
"scripts": {
"clean": "git clean -xdf .turbo node_modules",
"format": "prettier --check \"**/*.{ts,tsx}\"",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"prettier": "@kit/prettier-config",
"exports": {
".": "./src/index.ts"
},
"devDependencies": {
"@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*"
},
"eslintConfig": {
"root": true,
"extends": [
"@kit/eslint-config/base",
"@kit/eslint-config/react"
]
},
"typesVersions": {
"*": {
"*": [
"src/*"
]
}
}
}

View File

@@ -0,0 +1,2 @@
export * from './cms-client';
export * from './cms.type';

View File

@@ -0,0 +1,8 @@
{
"extends": "@kit/tsconfig/base.json",
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@@ -11,15 +11,17 @@
},
"prettier": "@kit/prettier-config",
"exports": {
".": "./src/index.ts"
".": "./src/index.ts",
"./renderer": "./src/content-renderer.tsx"
},
"devDependencies": {
"@kit/cms": "workspace:^",
"@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^",
"@types/node": "^22.5.2",
"@types/react": "^18.3.5",
"wp-types": "^4.66.1"
},
"eslintConfig": {

View File

@@ -1,3 +1,5 @@
import React from 'react';
export function WordpressContentRenderer(props: { content: unknown }) {
return <div dangerouslySetInnerHTML={{ __html: props.content as string }} />;
}

View File

@@ -5,10 +5,16 @@ import type {
WP_REST_API_Tag,
} from 'wp-types';
import { Cms, CmsClient } from '@kit/cms';
import { Cms, CmsClient } from '@kit/cms-types';
import GetTagsOptions = Cms.GetTagsOptions;
/**
* Creates a new WordpressClient instance.
*
* @param {string} apiUrl - The URL of the Wordpress API.
* @returns {WordpressClient} A new WordpressClient instance.
*/
export function createWordpressClient(
apiUrl = process.env.WORDPRESS_API_URL as string,
) {

32
pnpm-lock.yaml generated
View File

@@ -420,15 +420,24 @@ importers:
packages/cms/core:
devDependencies:
'@kit/cms-types':
specifier: workspace:^
version: link:../types
'@kit/eslint-config':
specifier: workspace:*
version: link:../../../tooling/eslint
'@kit/keystatic':
specifier: workspace:^
version: link:../keystatic
'@kit/prettier-config':
specifier: workspace:*
version: link:../../../tooling/prettier
'@kit/tsconfig':
specifier: workspace:*
version: link:../../../tooling/typescript
'@kit/wordpress':
specifier: workspace:^
version: link:../wordpress
'@types/node':
specifier: ^22.5.2
version: 22.5.2
@@ -445,9 +454,9 @@ importers:
specifier: ^0.4.0
version: 0.4.0(@types/react@18.3.5)(react@18.3.1)
devDependencies:
'@kit/cms':
'@kit/cms-types':
specifier: workspace:^
version: link:../core
version: link:../types
'@kit/eslint-config':
specifier: workspace:*
version: link:../../../tooling/eslint
@@ -473,11 +482,23 @@ importers:
specifier: ^3.23.8
version: 3.23.8
packages/cms/types:
devDependencies:
'@kit/eslint-config':
specifier: workspace:*
version: link:../../../tooling/eslint
'@kit/prettier-config':
specifier: workspace:*
version: link:../../../tooling/prettier
'@kit/tsconfig':
specifier: workspace:*
version: link:../../../tooling/typescript
packages/cms/wordpress:
devDependencies:
'@kit/cms':
'@kit/cms-types':
specifier: workspace:^
version: link:../core
version: link:../types
'@kit/eslint-config':
specifier: workspace:*
version: link:../../../tooling/eslint
@@ -493,6 +514,9 @@ importers:
'@types/node':
specifier: ^22.5.2
version: 22.5.2
'@types/react':
specifier: ^18.3.5
version: 18.3.5
wp-types:
specifier: ^4.66.1
version: 4.66.1