Skip to content

Commit

Permalink
feat: validate headless config with Yup config schema
Browse files Browse the repository at this point in the history
  • Loading branch information
sambrodie committed Aug 2, 2024
1 parent 14c398d commit e35476c
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 9 deletions.
45 changes: 44 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 9 additions & 8 deletions packages/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,31 @@
"lint": "eslint src"
},
"dependencies": {
"negotiator": "^0.6.3",
"@formatjs/intl-localematcher": "^0.5.4",
"deepmerge": "^4.3.1",
"@headstartwp/core": "^1.4.4",
"@isaacs/ttlcache": "^1.4.1",
"deepmerge": "^4.3.1",
"loader-utils": "^3.2.0",
"negotiator": "^0.6.3",
"schema-utils": "^4.0.0",
"@isaacs/ttlcache": "^1.4.1"
"yup": "^1.4.0"
},
"devDependencies": {
"@testing-library/dom": "^10.3.1",
"@testing-library/react": "^16.0.0",
"@types/jest": "^29.0.3",
"@types/react": "^18",
"@types/react-dom": "^18",
"copy-webpack-plugin": "^10.2.4",
"expect-type": "^0.15.0",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.0.3",
"jest-fetch-mock": "^3.0.3",
"next-router-mock": "^0.9.13",
"node-mocks-http": "^1.14.1",
"ts-jest": "^29.0.1",
"typescript": "^5.5.3",
"isomorphic-fetch": "^3.0.0",
"jest-fetch-mock": "^3.0.3",
"tsc-esm-fix": "^2.20.27",
"@types/react": "^18",
"@types/react-dom": "^18"
"typescript": "^5.5.3"
},
"peerDependencies": {
"next": ">= 12.0.0",
Expand Down
87 changes: 87 additions & 0 deletions packages/next/src/config/headlessConfigSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { object, string, mixed, number, array, boolean, lazy } from 'yup';

const headlessConfigSchema = object({
host: string(),
locale: string(),
sourceUrl: string(),
hostUrl: string(),
customPostTypes: lazy((value: any) => {
if (typeof value === 'function') {
return mixed();
}

return array().of(
object({
slug: string().required(),
endpoint: string().required(),
single: string(),
archive: string(),
matchSinglePath: boolean(),
}),
);
}),
customTaxonomies: lazy((value: any) => {
if (typeof value === 'function') {
return mixed();
}

return array().of(
object({
slug: string().required(),
endpoint: string().required(),
rewrite: string(),
restParam: string(),
matchArchivePath: boolean(),
}),
);
}),
redirectStrategy: string().oneOf(['404', 'none', 'always']),
useWordPressPlugin: boolean(),
integrations: object({
yoastSEO: object({
enabled: boolean(),
}),
polylang: object({
enabled: boolean(),
}),
}),
i18n: object({
locales: array(string()).required(),
defaultLocale: string().required(),
localeDetection: boolean(),
}).default(undefined),
preview: object({
alternativeAuthorizationHeader: boolean(),
usePostLinkForRedirect: boolean(),
}),
debug: object({
requests: boolean(),
redirects: boolean(),
devMode: boolean(),
}),
cache: object({
ttl: lazy((value: any) => {
if (typeof value === 'function') {
return mixed();
}

return number();
}),
enabled: lazy((value: any) => {
if (typeof value === 'function') {
return mixed();
}

return boolean();
}),
beforeSet: mixed(),
afterGet: mixed(),
cacheHandler: object({
set: mixed(),
get: mixed(),
}),
}),
sites: array(lazy(() => headlessConfigSchema.default(undefined))),
}).noUnknown();

export default headlessConfigSchema;
17 changes: 17 additions & 0 deletions packages/next/src/config/withHeadstartWPConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ConfigError, HeadlessConfig } from '@headstartwp/core';
import { NextConfig } from 'next';
import fs from 'fs';
import { ValidationError } from 'yup';
import { ModifySourcePlugin, ConcatOperation } from './plugins/ModifySourcePlugin';
import headlessConfigSchema from './headlessConfigSchema';

const LINARIA_EXTENSION = '.linaria.module.css';

Expand Down Expand Up @@ -106,6 +108,21 @@ export function withHeadstartWPConfig(
);
}

// Validate the config file, and log an error if it is invalid
try {
headlessConfigSchema.validateSync(headlessConfig, {
strict: true,
abortEarly: false,
stripUnknown: false,
});
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Error in the configuration file: ${error.errors.map(String)}`);
} else {
console.error('Unexpected error in the configuration file:', error);
}
}

const imageDomains: string[] = nextConfig.images?.domains ?? [];

const sites = headlessConfig.sites || [headlessConfig];
Expand Down

0 comments on commit e35476c

Please sign in to comment.