nextpress-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/acf-functions/core/acf-builder.ts +105 -0
- package/lib/acf-functions/core/acf-component-autoloader.ts +33 -0
- package/lib/acf-functions/core/acf-field-group-autoloader.ts +28 -0
- package/lib/acf-functions/services/define-field-group.ts +10 -0
- package/lib/acf-functions/services/define-layout.ts +10 -0
- package/lib/acf-functions/services/map-fields/helpers/map-choice-object.ts +20 -0
- package/lib/acf-functions/services/map-fields/map-fields.ts +359 -0
- package/lib/acf-functions/types/acf-field-group.d.ts +118 -0
- package/lib/acf-functions/types/acf-field.d.ts +2851 -0
- package/lib/acf-functions/types/acf-layout.d.ts +20 -0
- package/lib/acf-functions/types/acf-values.d.ts +4 -0
- package/lib/acf-functions/types/components/field-props.d.ts +189 -0
- package/lib/acf-functions/types/components/nextpress-component.d.ts +11 -0
- package/lib/ambient.d.ts +15 -0
- package/lib/entities/common.d.ts +30 -0
- package/lib/entities/option/option.interface.d.ts +8 -0
- package/lib/entities/option/option.ts +43 -0
- package/lib/entities/post/post.interface.d.ts +72 -0
- package/lib/entities/post/post.ts +209 -0
- package/lib/entities/term/term.interface.d.ts +8 -0
- package/lib/entities/term/term.ts +82 -0
- package/lib/entities/user/user.interface.d.ts +10 -0
- package/lib/entities/user/user.ts +86 -0
- package/lib/globals/entity-loader/entity-loader-base.ts +141 -0
- package/lib/globals/entity-loader/entity-loader.d.ts +50 -0
- package/lib/globals/entity-loader/option-loader.ts +56 -0
- package/lib/globals/entity-loader/post-loader.ts +59 -0
- package/lib/globals/entity-loader/term-loader.ts +59 -0
- package/lib/globals/entity-loader/user-loader.ts +60 -0
- package/lib/globals/get-field/get-field.ts +75 -0
- package/lib/globals/globals.ts +25 -0
- package/lib/globals/nextpress-config/nextpress-config.interface.d.ts +18 -0
- package/lib/globals/nextpress-config/nextpress-config.ts +7 -0
- package/lib/globals/queried-object/queried-object.ts +124 -0
- package/lib/repository/optionquery/option-query-args.d.ts +21 -0
- package/lib/repository/optionquery/option-query.ts +29 -0
- package/lib/repository/postquery/post-query-args.d.ts +108 -0
- package/lib/repository/postquery/post-query.ts +281 -0
- package/lib/repository/termquery/term-query-args.d.ts +61 -0
- package/lib/repository/termquery/term-query.ts +243 -0
- package/lib/repository/userquery/user-query-args.d.ts +75 -0
- package/lib/repository/userquery/user-query.ts +195 -0
- package/lib/router/helpers.ts +20 -0
- package/lib/router/nextpress-layout.tsx +48 -0
- package/lib/router/nextpress-proxy.ts +45 -0
- package/lib/router/nextpress-static-params.ts +62 -0
- package/lib/router/router.tsx +61 -0
- package/lib/router/routes/api/api-get-admin-bar.ts +29 -0
- package/lib/router/routes/api/api-get-draft-mode.ts +47 -0
- package/lib/router/routes/api/api-get-field-groups.ts +29 -0
- package/lib/router/routes/api/api-post-revalidate.ts +30 -0
- package/lib/router/routes/api/helpers.ts +17 -0
- package/lib/router/routes/author-archive.tsx +75 -0
- package/lib/router/routes/not-found-page.tsx +22 -0
- package/lib/router/routes/post-index-page.tsx +58 -0
- package/lib/router/routes/singular-page.tsx +78 -0
- package/lib/router/routes/site-front-page.tsx +51 -0
- package/lib/router/routes/term-archive.tsx +93 -0
- package/lib/router/types.d.ts +9 -0
- package/lib/services/get-menu.ts +104 -0
- package/lib/services/get-theme-mods.ts +16 -0
- package/lib/services/metadata/get-blogname.ts +9 -0
- package/lib/services/metadata/get-favicon-url.ts +13 -0
- package/lib/services/metadata/get-language-attribute.ts +9 -0
- package/lib/services/services.ts +7 -0
- package/lib/services/utilities/capitalise-first-letter.ts +10 -0
- package/lib/services/utilities/esc-html.ts +12 -0
- package/lib/services/utilities/get-date-time-formatter.ts +49 -0
- package/lib/services/utilities/index.ts +0 -0
- package/lib/services/utilities/kses-post.ts +13 -0
- package/lib/services/utilities/process-url.ts +15 -0
- package/lib/template-heirarchy/_autoloader/template-autoloader.ts +54 -0
- package/lib/template-heirarchy/archive/archive.tsx +28 -0
- package/lib/template-heirarchy/archive/author.tsx +28 -0
- package/lib/template-heirarchy/archive/category.tsx +29 -0
- package/lib/template-heirarchy/archive/posttypearchive.tsx +33 -0
- package/lib/template-heirarchy/archive/tag.tsx +30 -0
- package/lib/template-heirarchy/archive/taxonomy.tsx +28 -0
- package/lib/template-heirarchy/home/home.tsx +28 -0
- package/lib/template-heirarchy/index.tsx +30 -0
- package/lib/template-heirarchy/not-found.tsx/not-found.tsx +28 -0
- package/lib/template-heirarchy/page/page.tsx +28 -0
- package/lib/template-heirarchy/page/posttype.tsx +34 -0
- package/lib/template-heirarchy/page/single.tsx +28 -0
- package/lib/template-heirarchy/page/singular.tsx +28 -0
- package/lib/ui/render-attachment-image.tsx +29 -0
- package/lib/ui/render-components.tsx +12 -0
- package/lib/ui/render-the-admin-bar.tsx +79 -0
- package/lib/ui/render-the-logo.tsx +24 -0
- package/lib/wpdb/wpdb.interface.d.ts +171 -0
- package/lib/wpdb/wpdb.ts +36 -0
- package/package.json +38 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { MetadataResult, RouteProps, TemplateResult } from "../types";
|
|
2
|
+
import { getPageNumber } from "../helpers";
|
|
3
|
+
import { notFound } from "next/navigation";
|
|
4
|
+
import { CategoryMetadata, CategoryTemplate } from "@/template-heirarchy/archive/category";
|
|
5
|
+
import { TagMetadata, TagTemplate } from "@/template-heirarchy/archive/tag";
|
|
6
|
+
import { TaxonomyMetadata, TaxonomyTemplate } from "@/template-heirarchy/archive/taxonomy";
|
|
7
|
+
import { queriedObjectState } from "@/globals/globals";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the term archive route and maps it to the template hierarchy to return Metadata.
|
|
11
|
+
* * Routing Behavior:
|
|
12
|
+
* - If the taxonomy is 'category', it routes to `CategoryMetadata`.
|
|
13
|
+
* - If the taxonomy is 'tag', it routes to `TagMetadata`.
|
|
14
|
+
* - For all other taxonomies, it routes to `TaxonomyMetadata`.
|
|
15
|
+
*
|
|
16
|
+
* @param {{ path: string[], metadata: true }} props - Route properties requesting metadata.
|
|
17
|
+
* @returns {Promise<MetadataResult>} The generated metadata.
|
|
18
|
+
*/
|
|
19
|
+
export function TermArchive(props: { path: string[], metadata: true }): Promise<MetadataResult>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the term archive route and maps it to the template hierarchy to return a Template.
|
|
23
|
+
* * Routing Behavior:
|
|
24
|
+
* - If the taxonomy is 'category', it routes to `CategoryTemplate`.
|
|
25
|
+
* - If the taxonomy is 'tag', it routes to `TagTemplate`.
|
|
26
|
+
* - For all other taxonomies, it routes to `TaxonomyTemplate`.
|
|
27
|
+
*
|
|
28
|
+
* @param {{ path: string[], metadata?: false }} props - Route properties requesting a template.
|
|
29
|
+
* @returns {Promise<TemplateResult>} The rendered template component.
|
|
30
|
+
*/
|
|
31
|
+
export function TermArchive(props: { path: string[], metadata?: false }): Promise<TemplateResult>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Core implementation for the term archive route.
|
|
35
|
+
* Retrieves posts for a specific taxonomy term and updates the queried object state.
|
|
36
|
+
*
|
|
37
|
+
* @param {RouteProps} props - Route properties including the path array and metadata flag.
|
|
38
|
+
* @returns {Promise<MetadataResult | TemplateResult>} The metadata or template result based on the hierarchy.
|
|
39
|
+
* @throws {Error} Throws a Next.js notFound error if the term does not exist.
|
|
40
|
+
*/
|
|
41
|
+
export async function TermArchive({ path, metadata = false }: RouteProps) {
|
|
42
|
+
const postsPerPage = Number(await getOption('posts_per_page')) ?? 10;
|
|
43
|
+
|
|
44
|
+
let page = getPageNumber(path);
|
|
45
|
+
if (page) {
|
|
46
|
+
path = path.slice(0, -2);
|
|
47
|
+
} else {
|
|
48
|
+
page = 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const taxonomy = path[0] ?? '';
|
|
52
|
+
const pathString = path.slice(1).join('/');
|
|
53
|
+
|
|
54
|
+
const termQuery = await termLoader.findAndPrime({
|
|
55
|
+
taxonomy,
|
|
56
|
+
path: `/${pathString}`
|
|
57
|
+
});
|
|
58
|
+
if (!termQuery.ids.length) notFound();
|
|
59
|
+
|
|
60
|
+
const postIds = await postLoader.findAndPrime({
|
|
61
|
+
termIn: termQuery.ids,
|
|
62
|
+
noFoundRows: false,
|
|
63
|
+
noPaging: false,
|
|
64
|
+
postType: 'post',
|
|
65
|
+
page: page,
|
|
66
|
+
postsPerPage: postsPerPage,
|
|
67
|
+
postStatus: 'publish',
|
|
68
|
+
orderBy: 'date'
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const terms = await getTerms(termQuery.ids);
|
|
72
|
+
const mainTerm = terms.find(term => term.slug === path[path.length - 1])!;
|
|
73
|
+
|
|
74
|
+
const currentQueriedObject = {
|
|
75
|
+
objectType: 'term',
|
|
76
|
+
posts: postIds.ids,
|
|
77
|
+
page,
|
|
78
|
+
pageCount: Math.ceil(postIds.count / postsPerPage),
|
|
79
|
+
mainTerm: mainTerm.termId,
|
|
80
|
+
terms: termQuery.ids
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const state = queriedObjectState();
|
|
84
|
+
state.currentState = currentQueriedObject;
|
|
85
|
+
|
|
86
|
+
if (taxonomy === 'category') {
|
|
87
|
+
return metadata ? await CategoryMetadata() : <CategoryTemplate/>;
|
|
88
|
+
} else if (taxonomy === 'tag') {
|
|
89
|
+
return metadata ? await TagMetadata() : <TagTemplate/>;
|
|
90
|
+
} else {
|
|
91
|
+
return metadata ? await TaxonomyMetadata() : <TaxonomyTemplate/>;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { IMenuItem } from "../entities/post/post.interface";
|
|
2
|
+
import { getThemeMods } from "./get-theme-mods";
|
|
3
|
+
|
|
4
|
+
type Menu = {
|
|
5
|
+
menuItem: IMenuItem,
|
|
6
|
+
children: Menu[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Retrieves a menu by its location identifier.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} menuLocation - The location identifier of the menu.
|
|
13
|
+
* @returns {Promise<Menu[] | undefined>} An array of menu items representing the menu tree, or undefined if the menu is not found.
|
|
14
|
+
*/
|
|
15
|
+
export async function getMenu(menuLocation: string): Promise<Menu[] | undefined> {
|
|
16
|
+
const navMenuLocations = await getThemeMods('nav_menu_locations');
|
|
17
|
+
if (!navMenuLocations || typeof navMenuLocations !== 'object') return;
|
|
18
|
+
|
|
19
|
+
const menuTermId = Number((navMenuLocations as Record<string, any>)[menuLocation]) || undefined;
|
|
20
|
+
if (!menuTermId) return;
|
|
21
|
+
|
|
22
|
+
const postQuery = await postLoader.findAndPrime({
|
|
23
|
+
termId: menuTermId,
|
|
24
|
+
postStatus: 'publish',
|
|
25
|
+
postType: 'nav_menu_item',
|
|
26
|
+
noFoundRows: true,
|
|
27
|
+
noPaging: true
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const menuItems: IMenuItem[] = (await getPosts(postQuery.ids)).sort((a, b) => a.menuOrder - b.menuOrder);
|
|
31
|
+
|
|
32
|
+
// Prime cache to get paths in bunch later
|
|
33
|
+
for (const item of menuItems) {
|
|
34
|
+
if (!item.menuItemAttributes) continue;
|
|
35
|
+
|
|
36
|
+
if (item.menuItemAttributes.type === 'post_type') {
|
|
37
|
+
postLoader.prime([item.menuItemAttributes.objectId])
|
|
38
|
+
} else if (item.menuItemAttributes.type === 'taxonomy') {
|
|
39
|
+
termLoader.prime([item.menuItemAttributes.objectId])
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const map = new Map<number, Menu>();
|
|
44
|
+
const tree: Menu[] = [];
|
|
45
|
+
|
|
46
|
+
for (const item of menuItems) {
|
|
47
|
+
if (!item.menuItemAttributes) continue;
|
|
48
|
+
|
|
49
|
+
const menuItemAttributes = { ...item.menuItemAttributes };
|
|
50
|
+
|
|
51
|
+
if (item.menuItemAttributes.type === 'post_type') {
|
|
52
|
+
const post = await getPost(menuItemAttributes.objectId);
|
|
53
|
+
|
|
54
|
+
menuItemAttributes.url = post?.path ?? '';
|
|
55
|
+
|
|
56
|
+
if (!menuItemAttributes.label) {
|
|
57
|
+
menuItemAttributes.label = post?.postTitle ?? '';
|
|
58
|
+
}
|
|
59
|
+
} else if (item.menuItemAttributes.type === 'taxonomy') {
|
|
60
|
+
const term = await getTerm(menuItemAttributes.objectId);
|
|
61
|
+
|
|
62
|
+
menuItemAttributes.url = term?.path ?? '';
|
|
63
|
+
|
|
64
|
+
if (!menuItemAttributes.label) {
|
|
65
|
+
menuItemAttributes.label = term?.name ?? '';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const safeItem: IMenuItem = {
|
|
70
|
+
...item,
|
|
71
|
+
menuItemAttributes
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const node: Menu = {
|
|
75
|
+
menuItem: safeItem,
|
|
76
|
+
children: []
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
map.set(item.ID, node);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const item of menuItems) {
|
|
83
|
+
if (!item.menuItemAttributes) continue;
|
|
84
|
+
|
|
85
|
+
const currentNode = map.get(item.ID);
|
|
86
|
+
if (!currentNode) continue;
|
|
87
|
+
|
|
88
|
+
const parentId = item.menuItemAttributes.parentId;
|
|
89
|
+
|
|
90
|
+
if (parentId === 0) {
|
|
91
|
+
tree.push(currentNode);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const parentNode = map.get(parentId);
|
|
96
|
+
|
|
97
|
+
if (parentNode) {
|
|
98
|
+
parentNode.children.push(currentNode);
|
|
99
|
+
map.set(parentNode.menuItem.ID, parentNode);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return tree;
|
|
104
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { unserialize } from "php-serialize";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves a theme modification value by key.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} key - The key of the theme modification to retrieve.
|
|
7
|
+
* @returns {Promise<unknown | undefined>} The theme modification value, or undefined if not found.
|
|
8
|
+
*/
|
|
9
|
+
export async function getThemeMods(key: string): Promise<unknown | undefined> {
|
|
10
|
+
const themeModOption = await getOption('theme_mods_nextpress_theme');
|
|
11
|
+
|
|
12
|
+
const themeMods: Record<string, unknown> | unknown[] = unserialize(themeModOption ?? 'a:0:{}');
|
|
13
|
+
if (Array.isArray(themeMods)) return;
|
|
14
|
+
|
|
15
|
+
return themeMods[key];
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves the blog name.
|
|
3
|
+
*
|
|
4
|
+
* @returns {Promise<string>} The blog name, defaulting to 'My Blog' if not found.
|
|
5
|
+
*/
|
|
6
|
+
export async function getBlogname(): Promise<string> {
|
|
7
|
+
const blognameOption = await getOption('blogname');
|
|
8
|
+
return blognameOption ?? 'My Blog';
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves the URL of the site favicon.
|
|
3
|
+
*
|
|
4
|
+
* @returns {Promise<string|undefined>} The URL of the favicon, or undefined if not set.
|
|
5
|
+
*/
|
|
6
|
+
export async function getFaviconURL(): Promise<string|undefined> {
|
|
7
|
+
const iconID = await getOption('site_icon');
|
|
8
|
+
if (!iconID) return;
|
|
9
|
+
|
|
10
|
+
const iconMediaPost = await getPost(Number(iconID));
|
|
11
|
+
|
|
12
|
+
return iconMediaPost?.guid;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieves the language attribute.
|
|
3
|
+
*
|
|
4
|
+
* @returns {Promise<string>} The language attribute, defaulting to 'en_US' if not found.
|
|
5
|
+
*/
|
|
6
|
+
export async function getLanguageAttributes(): Promise<string> {
|
|
7
|
+
const wplang = await getOption('WPLANG');
|
|
8
|
+
return wplang || 'en_US';
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capitalizes the first letter of a string.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} string - The string to process.
|
|
5
|
+
* @returns {string} The string with its first letter capitalized.
|
|
6
|
+
*/
|
|
7
|
+
export function capitalizeFirstLetter(string: string): string {
|
|
8
|
+
if (!string) return string;
|
|
9
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import DOMPurify from "isomorphic-dompurify";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sanitizes an HTML string.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} string - The string to sanitize.
|
|
7
|
+
* @returns {string} The sanitized string.
|
|
8
|
+
*/
|
|
9
|
+
export function escHtml(string: string) {
|
|
10
|
+
return DOMPurify.sanitize(string);
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getLanguageAttributes } from "../metadata/get-language-attribute";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a DateTimeFormat instance based on WordPress settings.
|
|
5
|
+
*
|
|
6
|
+
* @returns {Promise<Intl.DateTimeFormat>} The formatter instance.
|
|
7
|
+
*/
|
|
8
|
+
export async function getDateTimeFormatter(): Promise<Intl.DateTimeFormat> {
|
|
9
|
+
const dateFormat = await getOption('date_format') ?? 'F j, Y';
|
|
10
|
+
const lang = await getLanguageAttributes();
|
|
11
|
+
|
|
12
|
+
const dateTimeOptionsMap: Record<string, Intl.DateTimeFormatOptions> = {
|
|
13
|
+
'd': { day: '2-digit' },
|
|
14
|
+
'j': { day: 'numeric' },
|
|
15
|
+
|
|
16
|
+
'l': { weekday: 'long' },
|
|
17
|
+
'D': { weekday: 'short' },
|
|
18
|
+
|
|
19
|
+
'm': { month: '2-digit' },
|
|
20
|
+
'n': { month: 'numeric' },
|
|
21
|
+
'F': { month: 'long' },
|
|
22
|
+
'M': { month: 'short' },
|
|
23
|
+
|
|
24
|
+
'Y': { year: 'numeric' },
|
|
25
|
+
'y': { year: '2-digit' },
|
|
26
|
+
|
|
27
|
+
'a': { hour12: true },
|
|
28
|
+
'A': { hour12: true },
|
|
29
|
+
|
|
30
|
+
'g': { hour: 'numeric', hour12: true },
|
|
31
|
+
'h': { hour: '2-digit', hour12: true },
|
|
32
|
+
'G': { hour: 'numeric', hour12: false },
|
|
33
|
+
'H': { hour: '2-digit', hour12: false },
|
|
34
|
+
|
|
35
|
+
'i': { minute: '2-digit' },
|
|
36
|
+
's': { second: '2-digit' },
|
|
37
|
+
|
|
38
|
+
'T': { timeZoneName: 'short' }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const dateTimeOptions = dateFormat.split('').reduce((options, char) => {
|
|
42
|
+
if (dateTimeOptionsMap[char]) {
|
|
43
|
+
return { ...options, ...dateTimeOptionsMap[char] };
|
|
44
|
+
}
|
|
45
|
+
return options;
|
|
46
|
+
}, {} as Intl.DateTimeFormatOptions);
|
|
47
|
+
|
|
48
|
+
return new Intl.DateTimeFormat(lang.replace('_', '-'), dateTimeOptions);
|
|
49
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import parse from 'html-react-parser';
|
|
2
|
+
import DOMPurify from "isomorphic-dompurify";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sanitizes and parses an HTML string into React elements.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} html - The HTML string to process.
|
|
8
|
+
* @returns {string | JSX.Element | JSX.Element[]} The parsed React elements.
|
|
9
|
+
*/
|
|
10
|
+
export function wpKsesPost(html: string) {
|
|
11
|
+
const cleanHtml = DOMPurify.sanitize(html, { USE_PROFILES: { html: true } });
|
|
12
|
+
return parse(cleanHtml);
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processes a URL by stripping the site URL prefix if it is present.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} url - The URL to process.
|
|
5
|
+
* @returns {string} The processed URL.
|
|
6
|
+
*/
|
|
7
|
+
export function processURL(url: string): string {
|
|
8
|
+
if (!process.env.WP_SITE_URL) return url;
|
|
9
|
+
|
|
10
|
+
if (url.startsWith(process.env.WP_SITE_URL)) {
|
|
11
|
+
url = url.slice(process.env.WP_SITE_URL.length);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return url || '/';
|
|
15
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { Metadata } from 'next';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Loads a specified export suffix from a given template file.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} template - The base name of the template to load.
|
|
9
|
+
* @param {'Metadata' | 'Template'} exportSuffix - The suffix of the export to retrieve from the file.
|
|
10
|
+
* @returns {Promise<any | undefined>} The requested export, or undefined if not found or an error occurs.
|
|
11
|
+
*/
|
|
12
|
+
async function loadFile(template: string, exportSuffix: 'Metadata' | 'Template'): Promise<any | undefined> {
|
|
13
|
+
const absolutePath = path.join(process.cwd(), 'src', 'app', '_templates');
|
|
14
|
+
const files = fs.readdirSync(absolutePath);
|
|
15
|
+
|
|
16
|
+
const imports: any[] = [];
|
|
17
|
+
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
if (file !== (`${template.toLowerCase()}.tsx`)) continue;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const imported = await import(`@/app/_templates/${file}`);
|
|
23
|
+
|
|
24
|
+
const exported = imported[`${template}${exportSuffix}`];
|
|
25
|
+
|
|
26
|
+
imports.push(exported);
|
|
27
|
+
} catch (_error) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return imports[0];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loads the exported metadata configuration for a given template.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} template - The name of the template file to load metadata from.
|
|
39
|
+
* @returns {Promise<Metadata | undefined>} The metadata object, or undefined if the metadata does not exist.
|
|
40
|
+
*/
|
|
41
|
+
export async function loadMetadata(template: string): Promise<Metadata | undefined> {
|
|
42
|
+
const metadataFn = await loadFile(template, 'Metadata');
|
|
43
|
+
if (metadataFn) {
|
|
44
|
+
return typeof metadataFn === 'function' ? metadataFn() : metadataFn;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function loadTemplate(template: string): Promise<React.ComponentType<any> | undefined> {
|
|
49
|
+
const element = await loadFile(template, 'Template');
|
|
50
|
+
if (element) {
|
|
51
|
+
return element as React.ComponentType<any>;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { IndexMetadata, IndexTemplate } from "..";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Archive template.
|
|
8
|
+
* Fallback: Index
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function ArchiveMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Archive');
|
|
14
|
+
return {...(await IndexMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Archive template. Falls back to the Index template if not found.
|
|
19
|
+
* Fallback: Index
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function ArchiveTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Archive = await loadTemplate('Archive');
|
|
25
|
+
if (!Archive) return <IndexTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Archive/>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { ArchiveMetadata, ArchiveTemplate } from "./archive";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Author template.
|
|
8
|
+
* Fallback: Archive
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function AuthorMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Author');
|
|
14
|
+
return {...(await ArchiveMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Author template. Falls back to the Archive template if not found.
|
|
19
|
+
* Fallback: Archive
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function AuthorTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Author = await loadTemplate('Author');
|
|
25
|
+
if (!Author) return <ArchiveTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Author/>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { ArchiveMetadata, ArchiveTemplate } from "./archive";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Category template.
|
|
8
|
+
* Fallback: Archive
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function CategoryMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Category');
|
|
14
|
+
return {...(await ArchiveMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Category template. Falls back to the Archive template if not found.
|
|
19
|
+
* Fallback: Archive
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function CategoryTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Category = await loadTemplate('Category');
|
|
25
|
+
if (!Category) return <ArchiveTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Category/>;
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { ArchiveMetadata, ArchiveTemplate } from "./archive";
|
|
5
|
+
import { capitalizeFirstLetter } from "@/services/utilities/capitalise-first-letter";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Loads the metadata for a Post Type Archive template based on the given post type.
|
|
9
|
+
* Fallback: Archive
|
|
10
|
+
*
|
|
11
|
+
* @param {object} props - The properties for loading metadata.
|
|
12
|
+
* @param {string} props.postType - The post type to load the metadata for.
|
|
13
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
14
|
+
*/
|
|
15
|
+
export async function PostTypeArchiveMetadata({postType}: {postType: string}): Promise<Metadata> {
|
|
16
|
+
const metadata = await loadMetadata(capitalizeFirstLetter(postType));
|
|
17
|
+
return {...(await ArchiveMetadata()), ...metadata};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Renders a Post Type Archive template. Falls back to the Archive template if not found.
|
|
22
|
+
* Fallback: Archive
|
|
23
|
+
*
|
|
24
|
+
* @param {object} props - The properties for rendering the template.
|
|
25
|
+
* @param {string} props.postType - The post type to load the template for.
|
|
26
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
27
|
+
*/
|
|
28
|
+
export async function PostTypeArchiveTemplate({postType}: {postType: string}): Promise<JSX.Element> {
|
|
29
|
+
const PostTypeArchive = await loadTemplate(capitalizeFirstLetter(postType));
|
|
30
|
+
if (!PostTypeArchive) return <ArchiveTemplate/>
|
|
31
|
+
|
|
32
|
+
return <PostTypeArchive/>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { ArchiveMetadata, ArchiveTemplate } from "./archive";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Tag template.
|
|
8
|
+
* Fallback: Archive
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function TagMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Tag');
|
|
14
|
+
return {...(await ArchiveMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Tag template. Falls back to the Archive template if not found.
|
|
19
|
+
* Fallback: Archive
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function TagTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Category = await loadTemplate('Tag');
|
|
25
|
+
if (!Category) return <ArchiveTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Category/>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { ArchiveMetadata, ArchiveTemplate } from "./archive";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Taxonomy template.
|
|
8
|
+
* Fallback: Archive
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function TaxonomyMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Taxonomy');
|
|
14
|
+
return {...(await ArchiveMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Taxonomy template. Falls back to the Archive template if not found.
|
|
19
|
+
* Fallback: Archive
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function TaxonomyTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Taxonomy = await loadTemplate('Taxonomy');
|
|
25
|
+
if (!Taxonomy) return <ArchiveTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Taxonomy/>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "../_autoloader/template-autoloader";
|
|
4
|
+
import { IndexMetadata, IndexTemplate } from "..";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Loads the metadata for the Home template.
|
|
8
|
+
* Fallback: Index
|
|
9
|
+
*
|
|
10
|
+
* @returns {Promise<Metadata>} The merged metadata object.
|
|
11
|
+
*/
|
|
12
|
+
export async function HomeMetadata(): Promise<Metadata> {
|
|
13
|
+
const metadata = await loadMetadata('Home');
|
|
14
|
+
return {...(await IndexMetadata()), ...metadata};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Renders the Home template. Falls back to the Index template if not found.
|
|
19
|
+
* Fallback: Index
|
|
20
|
+
*
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function HomeTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Home = await loadTemplate('Home');
|
|
25
|
+
if (!Home) return <IndexTemplate/>
|
|
26
|
+
|
|
27
|
+
return <Home/>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Metadata } from "next";
|
|
2
|
+
import { JSX } from "react";
|
|
3
|
+
import { loadMetadata, loadTemplate } from "./_autoloader/template-autoloader";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Loads the metadata for the Index template.
|
|
7
|
+
* Fallback: None
|
|
8
|
+
*
|
|
9
|
+
* @returns {Promise<Metadata>} The loaded metadata object.
|
|
10
|
+
*/
|
|
11
|
+
export async function IndexMetadata(): Promise<Metadata> {
|
|
12
|
+
const metadata = await loadMetadata('Index');
|
|
13
|
+
return metadata ?? {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Renders the Index template.
|
|
18
|
+
* Fallback: None
|
|
19
|
+
*
|
|
20
|
+
* @throws {Error} Throws if the Index template cannot be found in the _templates directory.
|
|
21
|
+
* @returns {Promise<JSX.Element>} The rendered template component.
|
|
22
|
+
*/
|
|
23
|
+
export async function IndexTemplate(): Promise<JSX.Element> {
|
|
24
|
+
const Index = await loadTemplate('Index');
|
|
25
|
+
if (!Index) {
|
|
26
|
+
throw new Error('Nextpress needs an index.tsx file in the template heirarchy in the _templates directory to function');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return <Index/>;
|
|
30
|
+
}
|