@earlyseo/blog 1.0.1
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/README.md +304 -0
- package/dist/client.d.ts +22 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +65 -0
- package/dist/client.js.map +1 -0
- package/dist/css.d.ts +16 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +186 -0
- package/dist/css.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/next/back-link.d.ts +12 -0
- package/dist/next/back-link.d.ts.map +1 -0
- package/dist/next/back-link.js +15 -0
- package/dist/next/back-link.js.map +1 -0
- package/dist/next/blog-list-page.d.ts +22 -0
- package/dist/next/blog-list-page.d.ts.map +1 -0
- package/dist/next/blog-list-page.js +49 -0
- package/dist/next/blog-list-page.js.map +1 -0
- package/dist/next/blog-post-page.d.ts +65 -0
- package/dist/next/blog-post-page.d.ts.map +1 -0
- package/dist/next/blog-post-page.js +103 -0
- package/dist/next/blog-post-page.js.map +1 -0
- package/dist/next/index.d.ts +5 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +11 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/blog-list.d.ts +27 -0
- package/dist/react/blog-list.d.ts.map +1 -0
- package/dist/react/blog-list.js +49 -0
- package/dist/react/blog-list.js.map +1 -0
- package/dist/react/blog-post.d.ts +35 -0
- package/dist/react/blog-post.d.ts.map +1 -0
- package/dist/react/blog-post.js +41 -0
- package/dist/react/blog-post.js.map +1 -0
- package/dist/react/index.d.ts +8 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +15 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/provider.d.ts +30 -0
- package/dist/react/provider.d.ts.map +1 -0
- package/dist/react/provider.js +34 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/styles.d.ts +11 -0
- package/dist/react/styles.d.ts.map +1 -0
- package/dist/react/styles.js +16 -0
- package/dist/react/styles.js.map +1 -0
- package/dist/react/use-article.d.ts +17 -0
- package/dist/react/use-article.d.ts.map +1 -0
- package/dist/react/use-article.js +53 -0
- package/dist/react/use-article.js.map +1 -0
- package/dist/react/use-articles.d.ts +22 -0
- package/dist/react/use-articles.d.ts.map +1 -0
- package/dist/react/use-articles.js +59 -0
- package/dist/react/use-articles.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +68 -0
- package/src/cli/init.mjs +188 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ArticleListItem, EarlySeoConfig } from "../types";
|
|
3
|
+
export interface BlogListPageOptions extends EarlySeoConfig {
|
|
4
|
+
/** Page title (default: "Blog") */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Base path for article links (default: "/blog") */
|
|
7
|
+
basePath?: string;
|
|
8
|
+
/** Custom card renderer */
|
|
9
|
+
renderCard?: (article: ArticleListItem, basePath: string) => React.ReactNode;
|
|
10
|
+
/** Additional fetch options (e.g. `{ next: { revalidate: 60 } }`) */
|
|
11
|
+
fetchInit?: RequestInit;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a Next.js Server Component for the /blog listing page.
|
|
15
|
+
* Supports `?page=2` query param for pagination automatically.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createBlogListPage(options: BlogListPageOptions): ({ searchParams, }: {
|
|
18
|
+
searchParams?: Promise<{
|
|
19
|
+
page?: string;
|
|
20
|
+
}>;
|
|
21
|
+
}) => Promise<import("react/jsx-runtime").JSX.Element>;
|
|
22
|
+
//# sourceMappingURL=blog-list-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-list-page.d.ts","sourceRoot":"","sources":["../../src/next/blog-list-page.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAIhE,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAC7E,qEAAqE;IACrE,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AA8CD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,IAY1B,mBAEhC;IACD,YAAY,CAAC,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3C,sDA+DF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
// @earlyseo/blog — Next.js Blog List Page (Server Component)
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Drop-in page for /blog. Fetches paginated article list from our CDN.
|
|
6
|
+
// Zero database, zero API — just a single JSON fetch per page.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// // app/blog/page.tsx
|
|
10
|
+
// import { createBlogListPage, createBlogListMetadata } from '@earlyseo/blog/next';
|
|
11
|
+
//
|
|
12
|
+
// export default createBlogListPage({ siteId: process.env.EARLYSEO_SITE_ID! });
|
|
13
|
+
// export const generateMetadata = createBlogListMetadata({ title: "Blog" });
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
import React from "react";
|
|
16
|
+
import { EarlySeoClient } from "../client";
|
|
17
|
+
import { ARTICLE_CSS, BLOG_CSS } from "../css";
|
|
18
|
+
import { BackLink } from "./back-link";
|
|
19
|
+
function formatDate(iso) {
|
|
20
|
+
try {
|
|
21
|
+
return new Date(iso).toLocaleDateString("en-US", {
|
|
22
|
+
year: "numeric",
|
|
23
|
+
month: "long",
|
|
24
|
+
day: "numeric",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return iso;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function DefaultCard({ article, basePath }) {
|
|
32
|
+
const href = `${basePath}/${article.slug}`;
|
|
33
|
+
return (_jsx("article", { className: "earlyseo-blog-card", children: _jsxs("a", { href: href, children: [article.imageUrl && (_jsx("img", { className: "earlyseo-blog-card-image", src: article.imageUrl, alt: article.imageAlt ?? article.title, loading: "lazy" })), _jsxs("div", { className: "earlyseo-blog-card-body", children: [_jsx("h2", { className: "earlyseo-blog-card-title", children: article.title }), article.metaDescription && (_jsx("p", { className: "earlyseo-blog-card-desc", children: article.metaDescription })), _jsxs("div", { className: "earlyseo-blog-card-meta", children: [_jsx("time", { dateTime: article.createdAt, children: formatDate(article.createdAt) }), article.tags.slice(0, 3).map((tag) => (_jsx("span", { className: "earlyseo-blog-tag", children: tag }, tag)))] })] })] }) }));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Creates a Next.js Server Component for the /blog listing page.
|
|
37
|
+
* Supports `?page=2` query param for pagination automatically.
|
|
38
|
+
*/
|
|
39
|
+
export function createBlogListPage(options) {
|
|
40
|
+
const { siteId, cdnBaseUrl, title = "Blog", basePath = "/blog", renderCard, fetchInit, } = options;
|
|
41
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl }, fetchInit);
|
|
42
|
+
return async function BlogListPage({ searchParams, }) {
|
|
43
|
+
const params = searchParams ? await searchParams : {};
|
|
44
|
+
const page = Math.max(1, parseInt(params.page ?? "1", 10));
|
|
45
|
+
const data = await client.getListPage(page);
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { dangerouslySetInnerHTML: { __html: `${ARTICLE_CSS}\n${BLOG_CSS}` } }), _jsxs("div", { className: "earlyseo-blog", children: [_jsx("nav", { style: { marginBottom: "1rem" }, children: _jsx(BackLink, { style: { fontSize: "0.9rem", opacity: 0.6 } }) }), _jsxs("div", { className: "earlyseo-blog-header", children: [_jsx("h1", { children: title }), data && data.totalArticles > 0 && (_jsxs("p", { style: { opacity: 0.6, fontSize: "0.9rem" }, children: [data.totalArticles, " article", data.totalArticles !== 1 ? "s" : ""] }))] }), !data || data.articles.length === 0 ? (_jsx("div", { className: "earlyseo-blog-empty", children: "No articles yet. Check back soon!" })) : (_jsx("div", { className: "earlyseo-blog-grid", children: data.articles.map((article) => renderCard ? (_jsx(React.Fragment, { children: renderCard(article, basePath) }, article.slug)) : (_jsx(DefaultCard, { article: article, basePath: basePath }, article.slug))) })), data && data.totalPages > 1 && (_jsxs("div", { className: "earlyseo-blog-pagination", children: [page > 1 ? (_jsx("a", { href: `${basePath}?page=${page - 1}`, children: _jsx("button", { type: "button", children: "\u2190 Previous" }) })) : (_jsx("button", { type: "button", disabled: true, children: "\u2190 Previous" })), _jsxs("span", { style: { padding: "0.5rem 0.75rem", fontSize: "0.85rem", opacity: 0.6 }, children: ["Page ", page, " of ", data.totalPages] }), page < data.totalPages ? (_jsx("a", { href: `${basePath}?page=${page + 1}`, children: _jsx("button", { type: "button", children: "Next \u2192" }) })) : (_jsx("button", { type: "button", disabled: true, children: "Next \u2192" }))] }))] })] }));
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=blog-list-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-list-page.js","sourceRoot":"","sources":["../../src/next/blog-list-page.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { EarlySeoConfig } from "../types";
|
|
3
|
+
export interface BlogPostPageOptions extends EarlySeoConfig {
|
|
4
|
+
/** Base path for back link (default: "/blog") */
|
|
5
|
+
basePath?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Choose which HTML to render:
|
|
8
|
+
* - "styled" (default): Uses contentHtml with .earlyseo-article wrapper
|
|
9
|
+
* - "raw": Uses contentRawHtml — inherits your site's styles
|
|
10
|
+
*/
|
|
11
|
+
htmlMode?: "styled" | "raw";
|
|
12
|
+
/** Site name for metadata (used in og:site_name) */
|
|
13
|
+
siteName?: string;
|
|
14
|
+
/** Additional fetch options (e.g. `{ next: { revalidate: 60 } }`) */
|
|
15
|
+
fetchInit?: RequestInit;
|
|
16
|
+
/** Custom article body renderer */
|
|
17
|
+
renderArticle?: (article: {
|
|
18
|
+
title: string;
|
|
19
|
+
html: string;
|
|
20
|
+
imageUrl?: string;
|
|
21
|
+
imageAlt?: string;
|
|
22
|
+
tags: string[];
|
|
23
|
+
createdAt: string;
|
|
24
|
+
metaDescription: string;
|
|
25
|
+
}) => React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates a Next.js Server Component for the /blog/[slug] detail page.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createBlogPostPage(options: BlogPostPageOptions): ({ params, }: {
|
|
31
|
+
params: Promise<{
|
|
32
|
+
slug: string;
|
|
33
|
+
}>;
|
|
34
|
+
}) => Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a generateMetadata function for the /blog/[slug] page.
|
|
37
|
+
* Provides full SEO metadata including Open Graph and Twitter cards.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createBlogPostMetadata(options: EarlySeoConfig & {
|
|
40
|
+
siteName?: string;
|
|
41
|
+
basePath?: string;
|
|
42
|
+
fetchInit?: RequestInit;
|
|
43
|
+
}): ({ params, }: {
|
|
44
|
+
params: Promise<{
|
|
45
|
+
slug: string;
|
|
46
|
+
}>;
|
|
47
|
+
}) => Promise<Record<string, unknown>>;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a generateMetadata function for the /blog listing page.
|
|
50
|
+
*/
|
|
51
|
+
export declare function createBlogListMetadata(options: {
|
|
52
|
+
title?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
siteName?: string;
|
|
55
|
+
}): () => Promise<{
|
|
56
|
+
title: string;
|
|
57
|
+
description: string;
|
|
58
|
+
openGraph: {
|
|
59
|
+
siteName?: string | undefined;
|
|
60
|
+
title: string;
|
|
61
|
+
description: string;
|
|
62
|
+
type: string;
|
|
63
|
+
};
|
|
64
|
+
}>;
|
|
65
|
+
//# sourceMappingURL=blog-post-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-post-page.d.ts","sourceRoot":"","sources":["../../src/next/blog-post-page.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI/C,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,mCAAmC;IACnC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,CAAC;KACzB,KAAK,KAAK,CAAC,SAAS,CAAC;CACvB;AAcD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,IAY1B,aAEhC;IACD,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC,0IAuEF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB,IAIwC,aAEpC;IACD,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnC,sCAqCF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;;;;;;;;;GAmBA"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { EarlySeoClient } from "../client";
|
|
3
|
+
import { ARTICLE_CSS, BLOG_CSS } from "../css";
|
|
4
|
+
import { BackLink } from "./back-link";
|
|
5
|
+
function formatDate(iso) {
|
|
6
|
+
try {
|
|
7
|
+
return new Date(iso).toLocaleDateString("en-US", {
|
|
8
|
+
year: "numeric",
|
|
9
|
+
month: "long",
|
|
10
|
+
day: "numeric",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return iso;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Next.js Server Component for the /blog/[slug] detail page.
|
|
19
|
+
*/
|
|
20
|
+
export function createBlogPostPage(options) {
|
|
21
|
+
const { siteId, cdnBaseUrl, basePath = "/blog", htmlMode = "styled", renderArticle, fetchInit, } = options;
|
|
22
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl }, fetchInit);
|
|
23
|
+
return async function BlogPostPage({ params, }) {
|
|
24
|
+
const { slug } = await params;
|
|
25
|
+
const article = await client.getArticle(slug);
|
|
26
|
+
if (!article) {
|
|
27
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { dangerouslySetInnerHTML: { __html: BLOG_CSS } }), _jsx("div", { className: "earlyseo-blog-post", children: _jsxs("div", { className: "earlyseo-blog-empty", children: [_jsx("p", { children: "Article not found." }), _jsx(BackLink, { fallbackHref: basePath, children: "\u2190 Go back" })] }) })] }));
|
|
28
|
+
}
|
|
29
|
+
const htmlContent = htmlMode === "raw" ? article.contentRawHtml : article.contentHtml;
|
|
30
|
+
if (renderArticle) {
|
|
31
|
+
return renderArticle({
|
|
32
|
+
title: article.title,
|
|
33
|
+
html: htmlContent,
|
|
34
|
+
imageUrl: article.imageUrl,
|
|
35
|
+
imageAlt: article.imageAlt,
|
|
36
|
+
tags: article.tags,
|
|
37
|
+
createdAt: article.createdAt,
|
|
38
|
+
metaDescription: article.metaDescription,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { dangerouslySetInnerHTML: { __html: `${ARTICLE_CSS}\n${BLOG_CSS}` } }), _jsxs("div", { className: "earlyseo-blog-post", children: [_jsx("nav", { style: { marginBottom: "1.5rem" }, children: _jsx(BackLink, { fallbackHref: basePath, style: { fontSize: "0.9rem", opacity: 0.6 }, children: "\u2190 Go back" }) }), _jsxs("header", { className: "earlyseo-blog-post-header", children: [_jsx("h1", { children: article.title }), _jsxs("div", { className: "earlyseo-blog-post-meta", children: [_jsx("time", { dateTime: article.createdAt, children: formatDate(article.createdAt) }), article.tags.map((tag) => (_jsx("span", { className: "earlyseo-blog-tag", children: tag }, tag)))] })] }), article.imageUrl && (_jsx("img", { className: "earlyseo-blog-post-image", src: article.imageUrl, alt: article.imageAlt ?? article.title })), htmlMode === "styled" && article.contentCss && (_jsx("style", { dangerouslySetInnerHTML: { __html: article.contentCss } })), _jsx("div", { dangerouslySetInnerHTML: { __html: htmlContent } })] })] }));
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a generateMetadata function for the /blog/[slug] page.
|
|
46
|
+
* Provides full SEO metadata including Open Graph and Twitter cards.
|
|
47
|
+
*/
|
|
48
|
+
export function createBlogPostMetadata(options) {
|
|
49
|
+
const { siteId, cdnBaseUrl, siteName, fetchInit } = options;
|
|
50
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl }, fetchInit);
|
|
51
|
+
return async function generateMetadata({ params, }) {
|
|
52
|
+
const { slug } = await params;
|
|
53
|
+
const article = await client.getArticle(slug);
|
|
54
|
+
if (!article) {
|
|
55
|
+
return { title: "Article not found" };
|
|
56
|
+
}
|
|
57
|
+
const metadata = {
|
|
58
|
+
title: article.title,
|
|
59
|
+
description: article.metaDescription,
|
|
60
|
+
openGraph: {
|
|
61
|
+
title: article.title,
|
|
62
|
+
description: article.metaDescription,
|
|
63
|
+
type: "article",
|
|
64
|
+
publishedTime: article.createdAt,
|
|
65
|
+
modifiedTime: article.updatedAt,
|
|
66
|
+
tags: article.tags,
|
|
67
|
+
...(article.imageUrl && {
|
|
68
|
+
images: [{ url: article.imageUrl, alt: article.imageAlt ?? article.title }],
|
|
69
|
+
}),
|
|
70
|
+
...(siteName && { siteName }),
|
|
71
|
+
},
|
|
72
|
+
twitter: {
|
|
73
|
+
card: article.imageUrl ? "summary_large_image" : "summary",
|
|
74
|
+
title: article.title,
|
|
75
|
+
description: article.metaDescription,
|
|
76
|
+
...(article.imageUrl && { images: [article.imageUrl] }),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
if (article.canonicalUrl) {
|
|
80
|
+
metadata.alternates = { canonical: article.canonicalUrl };
|
|
81
|
+
}
|
|
82
|
+
return metadata;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Creates a generateMetadata function for the /blog listing page.
|
|
87
|
+
*/
|
|
88
|
+
export function createBlogListMetadata(options) {
|
|
89
|
+
const { title = "Blog", description = "Read our latest articles", siteName, } = options;
|
|
90
|
+
return async function generateMetadata() {
|
|
91
|
+
return {
|
|
92
|
+
title,
|
|
93
|
+
description,
|
|
94
|
+
openGraph: {
|
|
95
|
+
title,
|
|
96
|
+
description,
|
|
97
|
+
type: "website",
|
|
98
|
+
...(siteName && { siteName }),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=blog-post-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-post-page.js","sourceRoot":"","sources":["../../src/next/blog-post-page.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createBlogListPage, type BlogListPageOptions } from "./blog-list-page";
|
|
2
|
+
export { createBlogPostPage, createBlogPostMetadata, createBlogListMetadata, type BlogPostPageOptions, } from "./blog-post-page";
|
|
3
|
+
export { BackLink } from "./back-link";
|
|
4
|
+
export { EarlySeoClient } from "../client";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// @earlyseo/blog — Next.js Exports
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Server Component pages
|
|
5
|
+
export { createBlogListPage } from "./blog-list-page";
|
|
6
|
+
export { createBlogPostPage, createBlogPostMetadata, createBlogListMetadata, } from "./blog-post-page";
|
|
7
|
+
// Client components
|
|
8
|
+
export { BackLink } from "./back-link";
|
|
9
|
+
// Re-export client for convenience
|
|
10
|
+
export { EarlySeoClient } from "../client";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mCAAmC;AACnC,gFAAgF;AAEhF,yBAAyB;AACzB,OAAO,EAAE,kBAAkB,EAA4B,MAAM,kBAAkB,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,kBAAkB,CAAC;AAE1B,oBAAoB;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,mCAAmC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { ArticleListItem } from "../types";
|
|
3
|
+
export interface BlogListProps {
|
|
4
|
+
/** Starting page (default: 1) */
|
|
5
|
+
initialPage?: number;
|
|
6
|
+
/** Page title (default: "Blog") */
|
|
7
|
+
title?: string;
|
|
8
|
+
/** Custom class on the wrapper */
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Custom card renderer — use this for full control over card appearance */
|
|
11
|
+
renderCard?: (article: ArticleListItem, basePath: string) => ReactNode;
|
|
12
|
+
/** Custom empty state */
|
|
13
|
+
renderEmpty?: () => ReactNode;
|
|
14
|
+
/** Custom loading state */
|
|
15
|
+
renderLoading?: () => ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pre-styled blog listing page component.
|
|
19
|
+
* Fetches paginated articles from the EarlySEO CDN.
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { BlogList } from '@earlyseo/blog/react';
|
|
23
|
+
* <BlogList title="Our Blog" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function BlogList({ initialPage, title, className, renderCard, renderEmpty, renderLoading, }: BlogListProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=blog-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-list.d.ts","sourceRoot":"","sources":["../../src/react/blog-list.tsx"],"names":[],"mappings":"AAMA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC;IACvE,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,SAAS,CAAC;IAC9B,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,SAAS,CAAC;CACjC;AA8CD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,WAAe,EACf,KAAc,EACd,SAAS,EACT,UAAU,EACV,WAAW,EACX,aAAa,GACd,EAAE,aAAa,2CAwEf"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// @earlyseo/blog — <BlogList /> component
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
// Pre-styled blog listing that fetches paginated JSON from the CDN.
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { useEarlySeoContext } from "./provider";
|
|
9
|
+
import { useArticles } from "./use-articles";
|
|
10
|
+
function formatDate(iso) {
|
|
11
|
+
try {
|
|
12
|
+
return new Date(iso).toLocaleDateString("en-US", {
|
|
13
|
+
year: "numeric",
|
|
14
|
+
month: "long",
|
|
15
|
+
day: "numeric",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return iso;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function DefaultCard({ article, basePath }) {
|
|
23
|
+
const href = `${basePath}/${article.slug}`;
|
|
24
|
+
return (_jsx("article", { className: "earlyseo-blog-card", children: _jsxs("a", { href: href, children: [article.imageUrl && (_jsx("img", { className: "earlyseo-blog-card-image", src: article.imageUrl, alt: article.imageAlt ?? article.title, loading: "lazy" })), _jsxs("div", { className: "earlyseo-blog-card-body", children: [_jsx("h2", { className: "earlyseo-blog-card-title", children: article.title }), article.metaDescription && (_jsx("p", { className: "earlyseo-blog-card-desc", children: article.metaDescription })), _jsxs("div", { className: "earlyseo-blog-card-meta", children: [_jsx("time", { dateTime: article.createdAt, children: formatDate(article.createdAt) }), article.tags.slice(0, 3).map((tag) => (_jsx("span", { className: "earlyseo-blog-tag", children: tag }, tag)))] })] })] }) }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Pre-styled blog listing page component.
|
|
28
|
+
* Fetches paginated articles from the EarlySEO CDN.
|
|
29
|
+
*
|
|
30
|
+
* ```tsx
|
|
31
|
+
* import { BlogList } from '@earlyseo/blog/react';
|
|
32
|
+
* <BlogList title="Our Blog" />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function BlogList({ initialPage = 1, title = "Blog", className, renderCard, renderEmpty, renderLoading, }) {
|
|
36
|
+
const { basePath } = useEarlySeoContext();
|
|
37
|
+
const { articles, page, totalPages, totalArticles, loading, error, goToPage } = useArticles(initialPage);
|
|
38
|
+
if (loading) {
|
|
39
|
+
return renderLoading ? (_jsx(_Fragment, { children: renderLoading() })) : (_jsx("div", { className: `earlyseo-blog ${className ?? ""}`, children: _jsx("div", { className: "earlyseo-blog-empty", children: "Loading articles\u2026" }) }));
|
|
40
|
+
}
|
|
41
|
+
if (error) {
|
|
42
|
+
return (_jsx("div", { className: `earlyseo-blog ${className ?? ""}`, children: _jsx("div", { className: "earlyseo-blog-empty", children: "Failed to load articles." }) }));
|
|
43
|
+
}
|
|
44
|
+
if (articles.length === 0) {
|
|
45
|
+
return renderEmpty ? (_jsx(_Fragment, { children: renderEmpty() })) : (_jsxs("div", { className: `earlyseo-blog ${className ?? ""}`, children: [_jsx("div", { className: "earlyseo-blog-header", children: _jsx("h1", { children: title }) }), _jsx("div", { className: "earlyseo-blog-empty", children: "No articles yet. Check back soon!" })] }));
|
|
46
|
+
}
|
|
47
|
+
return (_jsxs("div", { className: `earlyseo-blog ${className ?? ""}`, children: [_jsxs("div", { className: "earlyseo-blog-header", children: [_jsx("h1", { children: title }), totalArticles > 0 && (_jsxs("p", { style: { opacity: 0.6, fontSize: "0.9rem" }, children: [totalArticles, " article", totalArticles !== 1 ? "s" : ""] }))] }), _jsx("div", { className: "earlyseo-blog-grid", children: articles.map((article) => renderCard ? (_jsx(React.Fragment, { children: renderCard(article, basePath) }, article.slug)) : (_jsx(DefaultCard, { article: article, basePath: basePath }, article.slug))) }), totalPages > 1 && (_jsxs("div", { className: "earlyseo-blog-pagination", children: [_jsx("button", { type: "button", disabled: page <= 1, onClick: () => goToPage(page - 1), children: "\u2190 Previous" }), _jsxs("span", { style: { padding: "0.5rem 0.75rem", fontSize: "0.85rem", opacity: 0.6 }, children: ["Page ", page, " of ", totalPages] }), _jsx("button", { type: "button", disabled: page >= totalPages, onClick: () => goToPage(page + 1), children: "Next \u2192" })] }))] }));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=blog-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-list.js","sourceRoot":"","sources":["../../src/react/blog-list.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { Article } from "../types";
|
|
3
|
+
export interface BlogPostProps {
|
|
4
|
+
/** Article slug to display */
|
|
5
|
+
slug: string;
|
|
6
|
+
/** Custom class on the wrapper */
|
|
7
|
+
className?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Choose which HTML to render:
|
|
10
|
+
* - "styled" (default): Uses contentHtml with .earlyseo-article wrapper + embedded CSS
|
|
11
|
+
* - "raw": Uses contentRawHtml — inherits your site's styles
|
|
12
|
+
*/
|
|
13
|
+
htmlMode?: "styled" | "raw";
|
|
14
|
+
/** Custom article renderer for full control */
|
|
15
|
+
renderArticle?: (article: Article) => ReactNode;
|
|
16
|
+
/** Custom not-found state */
|
|
17
|
+
renderNotFound?: () => ReactNode;
|
|
18
|
+
/** Custom loading state */
|
|
19
|
+
renderLoading?: () => ReactNode;
|
|
20
|
+
/** Back link URL (default: basePath) */
|
|
21
|
+
backHref?: string;
|
|
22
|
+
/** Back link text (default: "← Back to blog") */
|
|
23
|
+
backText?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pre-styled blog post detail component.
|
|
27
|
+
* Fetches the article JSON from the EarlySEO CDN by slug.
|
|
28
|
+
*
|
|
29
|
+
* ```tsx
|
|
30
|
+
* import { BlogPost } from '@earlyseo/blog/react';
|
|
31
|
+
* <BlogPost slug="my-article" />
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function BlogPost({ slug, className, htmlMode, renderArticle, renderNotFound, renderLoading, backHref, backText, }: BlogPostProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=blog-post.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-post.d.ts","sourceRoot":"","sources":["../../src/react/blog-post.tsx"],"names":[],"mappings":"AAMA,OAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC5B,+CAA+C;IAC/C,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,SAAS,CAAC;IAChD,6BAA6B;IAC7B,cAAc,CAAC,EAAE,MAAM,SAAS,CAAC;IACjC,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,SAAS,CAAC;IAChC,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAcD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,SAAS,EACT,QAAmB,EACnB,aAAa,EACb,cAAc,EACd,aAAa,EACb,QAAQ,EACR,QAA2B,GAC5B,EAAE,aAAa,2CAoEf"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEarlySeoContext } from "./provider";
|
|
4
|
+
import { useArticle } from "./use-article";
|
|
5
|
+
function formatDate(iso) {
|
|
6
|
+
try {
|
|
7
|
+
return new Date(iso).toLocaleDateString("en-US", {
|
|
8
|
+
year: "numeric",
|
|
9
|
+
month: "long",
|
|
10
|
+
day: "numeric",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return iso;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pre-styled blog post detail component.
|
|
19
|
+
* Fetches the article JSON from the EarlySEO CDN by slug.
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { BlogPost } from '@earlyseo/blog/react';
|
|
23
|
+
* <BlogPost slug="my-article" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function BlogPost({ slug, className, htmlMode = "styled", renderArticle, renderNotFound, renderLoading, backHref, backText = "← Back to blog", }) {
|
|
27
|
+
const { basePath } = useEarlySeoContext();
|
|
28
|
+
const { article, loading, error } = useArticle(slug);
|
|
29
|
+
if (loading) {
|
|
30
|
+
return renderLoading ? (_jsx(_Fragment, { children: renderLoading() })) : (_jsx("div", { className: `earlyseo-blog-post ${className ?? ""}`, children: _jsx("div", { className: "earlyseo-blog-empty", children: "Loading article\u2026" }) }));
|
|
31
|
+
}
|
|
32
|
+
if (error || !article) {
|
|
33
|
+
return renderNotFound ? (_jsx(_Fragment, { children: renderNotFound() })) : (_jsx("div", { className: `earlyseo-blog-post ${className ?? ""}`, children: _jsxs("div", { className: "earlyseo-blog-empty", children: [_jsx("p", { children: "Article not found." }), _jsx("a", { href: backHref ?? basePath, children: backText })] }) }));
|
|
34
|
+
}
|
|
35
|
+
if (renderArticle) {
|
|
36
|
+
return _jsx(_Fragment, { children: renderArticle(article) });
|
|
37
|
+
}
|
|
38
|
+
const htmlContent = htmlMode === "raw" ? article.contentRawHtml : article.contentHtml;
|
|
39
|
+
return (_jsxs("div", { className: `earlyseo-blog-post ${className ?? ""}`, children: [_jsx("nav", { style: { marginBottom: "1.5rem" }, children: _jsx("a", { href: backHref ?? basePath, style: { fontSize: "0.9rem", opacity: 0.6 }, children: backText }) }), _jsxs("header", { className: "earlyseo-blog-post-header", children: [_jsx("h1", { children: article.title }), _jsxs("div", { className: "earlyseo-blog-post-meta", children: [_jsx("time", { dateTime: article.createdAt, children: formatDate(article.createdAt) }), article.tags.map((tag) => (_jsx("span", { className: "earlyseo-blog-tag", children: tag }, tag)))] })] }), article.imageUrl && (_jsx("img", { className: "earlyseo-blog-post-image", src: article.imageUrl, alt: article.imageAlt ?? article.title })), htmlMode === "styled" && article.contentCss && (_jsx("style", { dangerouslySetInnerHTML: { __html: article.contentCss } })), _jsx("div", { dangerouslySetInnerHTML: { __html: htmlContent } })] }));
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=blog-post.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-post.js","sourceRoot":"","sources":["../../src/react/blog-post.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { EarlySeoProvider, type EarlySeoProviderProps } from "./provider";
|
|
2
|
+
export { useArticles, type UseArticlesResult } from "./use-articles";
|
|
3
|
+
export { useArticle, type UseArticleResult } from "./use-article";
|
|
4
|
+
export { BlogList, type BlogListProps } from "./blog-list";
|
|
5
|
+
export { BlogPost, type BlogPostProps } from "./blog-post";
|
|
6
|
+
export { ArticleStyles } from "./styles";
|
|
7
|
+
export { EarlySeoClient } from "../client";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// @earlyseo/blog — React Exports
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Context & provider
|
|
5
|
+
export { EarlySeoProvider } from "./provider";
|
|
6
|
+
// Hooks
|
|
7
|
+
export { useArticles } from "./use-articles";
|
|
8
|
+
export { useArticle } from "./use-article";
|
|
9
|
+
// Pre-styled components
|
|
10
|
+
export { BlogList } from "./blog-list";
|
|
11
|
+
export { BlogPost } from "./blog-post";
|
|
12
|
+
export { ArticleStyles } from "./styles";
|
|
13
|
+
// Re-export client for convenience
|
|
14
|
+
export { EarlySeoClient } from "../client";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iCAAiC;AACjC,gFAAgF;AAEhF,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAA8B,MAAM,YAAY,CAAC;AAE1E,QAAQ;AACR,OAAO,EAAE,WAAW,EAA0B,MAAM,gBAAgB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAyB,MAAM,eAAe,CAAC;AAElE,wBAAwB;AACxB,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,mCAAmC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { EarlySeoClient } from "../client";
|
|
3
|
+
interface EarlySeoContextValue {
|
|
4
|
+
client: EarlySeoClient;
|
|
5
|
+
basePath: string;
|
|
6
|
+
}
|
|
7
|
+
export interface EarlySeoProviderProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/** Your site ID from the EarlySEO dashboard */
|
|
10
|
+
siteId: string;
|
|
11
|
+
/** CDN base URL override (defaults to EarlySEO CDN) */
|
|
12
|
+
cdnBaseUrl?: string;
|
|
13
|
+
/** Base path for blog URLs (default: "/blog") */
|
|
14
|
+
basePath?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Provides EarlySEO blog data to all child components and hooks.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* import { EarlySeoProvider } from '@earlyseo/blog/react';
|
|
21
|
+
*
|
|
22
|
+
* <EarlySeoProvider siteId="your-site-id">
|
|
23
|
+
* <App />
|
|
24
|
+
* </EarlySeoProvider>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function EarlySeoProvider({ children, siteId, cdnBaseUrl, basePath, }: EarlySeoProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export declare function useEarlySeoContext(): EarlySeoContextValue;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAKA,OAAc,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,UAAU,oBAAoB;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAC;IACpB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,UAAU,EACV,QAAkB,GACnB,EAAE,qBAAqB,2CAOvB;AAED,wBAAgB,kBAAkB,IAAI,oBAAoB,CAMzD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// @earlyseo/blog — EarlySeo React Context Provider
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
import { createContext, useContext, useMemo } from "react";
|
|
7
|
+
import { EarlySeoClient } from "../client";
|
|
8
|
+
const EarlySeoContext = createContext(null);
|
|
9
|
+
/**
|
|
10
|
+
* Provides EarlySEO blog data to all child components and hooks.
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { EarlySeoProvider } from '@earlyseo/blog/react';
|
|
14
|
+
*
|
|
15
|
+
* <EarlySeoProvider siteId="your-site-id">
|
|
16
|
+
* <App />
|
|
17
|
+
* </EarlySeoProvider>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function EarlySeoProvider({ children, siteId, cdnBaseUrl, basePath = "/blog", }) {
|
|
21
|
+
const value = useMemo(() => {
|
|
22
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl });
|
|
23
|
+
return { client, basePath };
|
|
24
|
+
}, [siteId, cdnBaseUrl, basePath]);
|
|
25
|
+
return _jsx(EarlySeoContext.Provider, { value: value, children: children });
|
|
26
|
+
}
|
|
27
|
+
export function useEarlySeoContext() {
|
|
28
|
+
const ctx = useContext(EarlySeoContext);
|
|
29
|
+
if (!ctx) {
|
|
30
|
+
throw new Error("useEarlySeoContext must be used within an <EarlySeoProvider>.");
|
|
31
|
+
}
|
|
32
|
+
return ctx;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AACb,gFAAgF;AAChF,mDAAmD;AACnD,gFAAgF;AAEhF,OAAc,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAQ3C,MAAM,eAAe,GAAG,aAAa,CAA8B,IAAI,CAAC,CAAC;AAYzE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,UAAU,EACV,QAAQ,GAAG,OAAO,GACI;IACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;QACzB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC9B,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEnC,OAAO,KAAC,eAAe,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA4B,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,GAAG,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injects EarlySEO article and blog CSS into the page.
|
|
3
|
+
* Place this once near the root of your app (or inside a layout).
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* import { ArticleStyles } from '@earlyseo/blog/react';
|
|
7
|
+
* <ArticleStyles />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function ArticleStyles(): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/react/styles.tsx"],"names":[],"mappings":"AAUA;;;;;;;;GAQG;AACH,wBAAgB,aAAa,4CAO5B"}
|