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" ".": "./src/index.ts"
}, },
"devDependencies": { "devDependencies": {
"@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*", "@kit/eslint-config": "workspace:*",
"@kit/keystatic": "workspace:^",
"@kit/prettier-config": "workspace:*", "@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*", "@kit/tsconfig": "workspace:*",
"@kit/wordpress": "workspace:^",
"@types/node": "^22.5.2" "@types/node": "^22.5.2"
}, },
"eslintConfig": { "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; const CMS_CLIENT = process.env.CMS_CLIENT as CmsType;
@@ -11,21 +11,32 @@ export async function ContentRenderer({
content, content,
type = CMS_CLIENT, type = CMS_CLIENT,
}: ContentRendererProps) { }: 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) { switch (type) {
case 'keystatic': { case 'keystatic': {
const { KeystaticContentRenderer } = await import( const { KeystaticContentRenderer } = await import(
'../../keystatic/src/content-renderer' '@kit/keystatic/renderer'
); );
return <KeystaticContentRenderer content={content} />; return KeystaticContentRenderer;
} }
case 'wordpress': { case 'wordpress': {
const { WordpressContentRenderer } = await import( const { WordpressContentRenderer } = await import(
'../../wordpress/src/content-renderer' '@kit/wordpress/renderer'
); );
return <WordpressContentRenderer content={content} />; return WordpressContentRenderer;
} }
default: { default: {

View File

@@ -1,5 +1,9 @@
import { CmsClient } from './cms-client'; import { CmsClient, CmsType } from '@kit/cms-types';
import { CmsType } from './cms.type';
/**
* 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. * 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. * @throws {Error} If the specified CMS type is unknown.
*/ */
export async function createCmsClient( export async function createCmsClient(
type: CmsType = process.env.CMS_CLIENT as CmsType, type: CmsType = CMS_CLIENT,
): Promise<CmsClient> { ): Promise<CmsClient> {
return cmsClientFactory(type); 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) { switch (type) {
case 'wordpress': case 'wordpress': {
return getWordpressClient(); const { createWordpressClient } = await import('@kit/wordpress');
case 'keystatic': return createWordpressClient();
return getKeystaticClient(); }
case 'keystatic': {
const { createKeystaticClient } = await import('@kit/keystatic');
return createKeystaticClient();
}
default: default:
throw new Error(`Unknown CMS type`); 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 './create-cms-client';
export * from './cms.type';
export * from './content-renderer'; export * from './content-renderer';
export type { Cms };

View File

@@ -11,6 +11,7 @@
"prettier": "@kit/prettier-config", "prettier": "@kit/prettier-config",
"exports": { "exports": {
".": "./src/index.ts", ".": "./src/index.ts",
"./renderer": "./src/content-renderer.tsx",
"./admin": "./src/keystatic-admin.tsx", "./admin": "./src/keystatic-admin.tsx",
"./route-handler": "./src/keystatic-route-handler.ts" "./route-handler": "./src/keystatic-route-handler.ts"
}, },
@@ -20,7 +21,7 @@
"@markdoc/markdoc": "^0.4.0" "@markdoc/markdoc": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@kit/cms": "workspace:^", "@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*", "@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*", "@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "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 { 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'; 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() { export async function createKeystaticReader() {
switch (STORAGE_KIND) { switch (STORAGE_KIND) {
case 'local': { case 'local': {
if (process.env.NEXT_RUNTIME === 'nodejs') { if (process.env.NEXT_RUNTIME === 'nodejs') {
const { default: config } = await import('./keystatic.config');
const { createReader } = await import('@keystatic/core/reader'); const { createReader } = await import('@keystatic/core/reader');
return createReader(process.cwd(), config); return createReader(process.cwd(), keyStaticConfig);
} else { } else {
// we should never get here but the compiler requires the check // we should never get here but the compiler requires the check
// to ensure we don't parse the package at build time // to ensure we don't parse the package at build time
@@ -22,11 +26,11 @@ export async function createKeystaticReader() {
case 'github': case 'github':
case 'cloud': { case 'cloud': {
const { default: config } = await import('./keystatic.config');
const githubConfig = z const githubConfig = z
.object({ .object({
token: z.string(), token: z.string({
description: 'The GitHub token to use for authentication.',
}),
repo: z.custom<`${string}/${string}`>(), repo: z.custom<`${string}/${string}`>(),
pathPrefix: z.string().optional(), pathPrefix: z.string().optional(),
}) })
@@ -40,7 +44,7 @@ export async function createKeystaticReader() {
'@keystatic/core/reader/github' '@keystatic/core/reader/github'
); );
return createGitHubReader(config, githubConfig); return createGitHubReader(keyStaticConfig, githubConfig);
} }
default: default:

View File

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

View File

@@ -2,6 +2,6 @@
import { makePage } from '@keystatic/next/ui/app'; 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 React from 'react';
import { Cms, CmsClient } from '@kit/cms'; import { Cms, CmsClient } from '@kit/cms-types';
import { createKeystaticReader } from './create-reader'; import { createKeystaticReader } from './create-reader';
import { import {

View File

@@ -1,9 +1,9 @@
import { makeRouteHandler } from '@keystatic/next/route-handler'; import { makeRouteHandler } from '@keystatic/next/route-handler';
import config from './keystatic.config'; import { keyStaticConfig } from './keystatic.config';
const handlers = makeRouteHandler({ 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, pathPrefix: process.env.KEYSTATIC_PATH_PREFIX,
}); });
const keyStaticConfig = createKeyStaticConfig( export const keyStaticConfig = createKeyStaticConfig(
process.env.NEXT_PUBLIC_KEYSTATIC_CONTENT_PATH ?? '', process.env.NEXT_PUBLIC_KEYSTATIC_CONTENT_PATH ?? '',
); );
export default keyStaticConfig;
function getContentField() { function getContentField() {
return fields.markdoc({ return fields.markdoc({
label: 'Content', 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", "prettier": "@kit/prettier-config",
"exports": { "exports": {
".": "./src/index.ts" ".": "./src/index.ts",
"./renderer": "./src/content-renderer.tsx"
}, },
"devDependencies": { "devDependencies": {
"@kit/cms": "workspace:^", "@kit/cms-types": "workspace:^",
"@kit/eslint-config": "workspace:*", "@kit/eslint-config": "workspace:*",
"@kit/prettier-config": "workspace:*", "@kit/prettier-config": "workspace:*",
"@kit/tsconfig": "workspace:*", "@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:^", "@kit/ui": "workspace:^",
"@types/node": "^22.5.2", "@types/node": "^22.5.2",
"@types/react": "^18.3.5",
"wp-types": "^4.66.1" "wp-types": "^4.66.1"
}, },
"eslintConfig": { "eslintConfig": {

View File

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

View File

@@ -5,10 +5,16 @@ import type {
WP_REST_API_Tag, WP_REST_API_Tag,
} from 'wp-types'; } from 'wp-types';
import { Cms, CmsClient } from '@kit/cms'; import { Cms, CmsClient } from '@kit/cms-types';
import GetTagsOptions = Cms.GetTagsOptions; 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( export function createWordpressClient(
apiUrl = process.env.WORDPRESS_API_URL as string, apiUrl = process.env.WORDPRESS_API_URL as string,
) { ) {

32
pnpm-lock.yaml generated
View File

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