@earlyseo/blog 1.0.4 → 1.0.6
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 +47 -3
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +2 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/sitemap.d.ts +30 -0
- package/dist/next/sitemap.d.ts.map +1 -0
- package/dist/next/sitemap.js +76 -0
- package/dist/next/sitemap.js.map +1 -0
- package/package.json +1 -5
- package/src/cli/init.mjs +60 -6
package/README.md
CHANGED
|
@@ -20,13 +20,14 @@ Articles are authored in the EarlySEO dashboard and rendered on your site. No da
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm install @earlyseo/blog
|
|
23
|
-
npx earlyseo
|
|
23
|
+
npx @earlyseo/blog init
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
The CLI will detect your Next.js project, ask for your site ID, and generate:
|
|
27
27
|
- `app/blog/page.tsx` — Paginated blog listing
|
|
28
28
|
- `app/blog/[slug]/page.tsx` — Article detail with full SEO metadata
|
|
29
|
-
-
|
|
29
|
+
- `app/sitemap.ts` — Blog URLs added to sitemap automatically
|
|
30
|
+
- `.env.local` — `EARLYSEO_SITE_ID` and `NEXT_PUBLIC_BASE_URL` added automatically
|
|
30
31
|
|
|
31
32
|
That's it — run `npm run dev` and visit `/blog`.
|
|
32
33
|
|
|
@@ -43,6 +44,7 @@ npm install @earlyseo/blog
|
|
|
43
44
|
```env
|
|
44
45
|
# .env.local
|
|
45
46
|
EARLYSEO_SITE_ID=your-site-id
|
|
47
|
+
NEXT_PUBLIC_BASE_URL=https://example.com
|
|
46
48
|
```
|
|
47
49
|
|
|
48
50
|
### 3. Add the blog listing page
|
|
@@ -77,6 +79,39 @@ export const generateMetadata = createBlogPostMetadata({ siteId, siteName: "My S
|
|
|
77
79
|
|
|
78
80
|
That's it! Articles published from EarlySEO automatically appear on your blog.
|
|
79
81
|
|
|
82
|
+
### 5. Add sitemap entries
|
|
83
|
+
|
|
84
|
+
If you already have an existing `app/sitemap.ts`, merge blog entries into it:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { getBlogSitemapEntries } from "@earlyseo/blog/next";
|
|
88
|
+
import type { MetadataRoute } from "next";
|
|
89
|
+
|
|
90
|
+
const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
91
|
+
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "https://example.com";
|
|
92
|
+
|
|
93
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
94
|
+
const blogEntries = await getBlogSitemapEntries({ siteId, baseUrl });
|
|
95
|
+
|
|
96
|
+
return [
|
|
97
|
+
{ url: baseUrl, lastModified: new Date() },
|
|
98
|
+
...blogEntries,
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Or create a dedicated blog sitemap:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// app/blog/sitemap.ts
|
|
107
|
+
import { createBlogSitemap } from "@earlyseo/blog/next";
|
|
108
|
+
|
|
109
|
+
const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
110
|
+
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "https://example.com";
|
|
111
|
+
|
|
112
|
+
export default createBlogSitemap({ siteId, baseUrl });
|
|
113
|
+
```
|
|
114
|
+
|
|
80
115
|
---
|
|
81
116
|
|
|
82
117
|
## How It Works
|
|
@@ -280,6 +315,15 @@ interface Article {
|
|
|
280
315
|
canonicalUrl?: string;
|
|
281
316
|
version: number;
|
|
282
317
|
}
|
|
318
|
+
|
|
319
|
+
interface BlogSitemapOptions {
|
|
320
|
+
siteId: string;
|
|
321
|
+
baseUrl: string;
|
|
322
|
+
basePath?: string;
|
|
323
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
324
|
+
priority?: number;
|
|
325
|
+
fetchInit?: RequestInit;
|
|
326
|
+
}
|
|
283
327
|
```
|
|
284
328
|
|
|
285
329
|
### Imports
|
|
@@ -288,7 +332,7 @@ interface Article {
|
|
|
288
332
|
|-------------|----------|
|
|
289
333
|
| `@earlyseo/blog` | `EarlySeoClient`, core types, CSS constants |
|
|
290
334
|
| `@earlyseo/blog/react` | Provider, hooks, components |
|
|
291
|
-
| `@earlyseo/blog/next` | Next.js page creators, metadata helpers |
|
|
335
|
+
| `@earlyseo/blog/next` | Next.js page creators, metadata helpers, sitemap helpers |
|
|
292
336
|
| `@earlyseo/blog/css` | `ARTICLE_CSS`, `BLOG_CSS` |
|
|
293
337
|
|
|
294
338
|
---
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { createBlogListPage, type BlogListPageOptions } from "./blog-list-page";
|
|
2
2
|
export { createBlogPostPage, createBlogPostMetadata, createBlogListMetadata, type BlogPostPageOptions, } from "./blog-post-page";
|
|
3
3
|
export { BackLink } from "./back-link";
|
|
4
|
+
export { createBlogSitemap, getBlogSitemapEntries, type BlogSitemapOptions, type SitemapEntry, } from "./sitemap";
|
|
4
5
|
export { EarlySeoClient } from "../client";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/next/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
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,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/next/index.js
CHANGED
|
@@ -6,6 +6,8 @@ export { createBlogListPage } from "./blog-list-page";
|
|
|
6
6
|
export { createBlogPostPage, createBlogPostMetadata, createBlogListMetadata, } from "./blog-post-page";
|
|
7
7
|
// Client components
|
|
8
8
|
export { BackLink } from "./back-link";
|
|
9
|
+
// Sitemap
|
|
10
|
+
export { createBlogSitemap, getBlogSitemapEntries, } from "./sitemap";
|
|
9
11
|
// Re-export client for convenience
|
|
10
12
|
export { EarlySeoClient } from "../client";
|
|
11
13
|
//# sourceMappingURL=index.js.map
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +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"}
|
|
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,UAAU;AACV,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GAGtB,MAAM,WAAW,CAAC;AAEnB,mCAAmC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { EarlySeoConfig } from "../types";
|
|
2
|
+
export interface BlogSitemapOptions extends EarlySeoConfig {
|
|
3
|
+
/** Your site's public base URL (e.g. "https://example.com") */
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
/** Blog base path (default: "/blog") */
|
|
6
|
+
basePath?: string;
|
|
7
|
+
/** Change frequency hint for sitemap (default: "weekly") */
|
|
8
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
9
|
+
/** Priority hint for blog articles (default: 0.7) */
|
|
10
|
+
priority?: number;
|
|
11
|
+
/** Additional fetch options (e.g. `{ next: { revalidate: 60 } }`) */
|
|
12
|
+
fetchInit?: RequestInit;
|
|
13
|
+
}
|
|
14
|
+
export interface SitemapEntry {
|
|
15
|
+
url: string;
|
|
16
|
+
lastModified?: Date | string;
|
|
17
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
18
|
+
priority?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fetches all published blog articles and returns an array of sitemap entries.
|
|
22
|
+
* Use this when you want to merge blog entries into an existing sitemap.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getBlogSitemapEntries(options: BlogSitemapOptions): Promise<SitemapEntry[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a Next.js App Router sitemap function that returns blog article entries.
|
|
27
|
+
* Use as the default export of `app/blog/sitemap.ts` or merge into `app/sitemap.ts`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createBlogSitemap(options: BlogSitemapOptions): () => Promise<SitemapEntry[]>;
|
|
30
|
+
//# sourceMappingURL=sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/next/sitemap.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,eAAe,CAAC,EACZ,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;IACZ,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IAC7B,eAAe,CAAC,EACZ,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CA6CzB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,SAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// @earlyseo/blog — Next.js Sitemap Helper
|
|
3
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// Generates sitemap entries for all published blog articles.
|
|
5
|
+
// Designed for Next.js App Router `sitemap.ts` files.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// // app/sitemap.ts (or app/blog/sitemap.ts)
|
|
9
|
+
// import { createBlogSitemap } from "@earlyseo/blog/next";
|
|
10
|
+
//
|
|
11
|
+
// const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
12
|
+
// const blogSitemap = createBlogSitemap({ siteId, baseUrl: "https://example.com" });
|
|
13
|
+
// export default blogSitemap;
|
|
14
|
+
//
|
|
15
|
+
// // Or merge with your own sitemap entries:
|
|
16
|
+
// import { getBlogSitemapEntries } from "@earlyseo/blog/next";
|
|
17
|
+
//
|
|
18
|
+
// export default async function sitemap() {
|
|
19
|
+
// const blogEntries = await getBlogSitemapEntries({
|
|
20
|
+
// siteId: process.env.EARLYSEO_SITE_ID!,
|
|
21
|
+
// baseUrl: "https://example.com",
|
|
22
|
+
// });
|
|
23
|
+
// return [
|
|
24
|
+
// { url: "https://example.com", lastModified: new Date() },
|
|
25
|
+
// ...blogEntries,
|
|
26
|
+
// ];
|
|
27
|
+
// }
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
29
|
+
import { EarlySeoClient } from "../client";
|
|
30
|
+
/**
|
|
31
|
+
* Fetches all published blog articles and returns an array of sitemap entries.
|
|
32
|
+
* Use this when you want to merge blog entries into an existing sitemap.
|
|
33
|
+
*/
|
|
34
|
+
export async function getBlogSitemapEntries(options) {
|
|
35
|
+
const { siteId, cdnBaseUrl, baseUrl, basePath = "/blog", changeFrequency = "weekly", priority = 0.7, fetchInit, } = options;
|
|
36
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl }, fetchInit);
|
|
37
|
+
const entries = [];
|
|
38
|
+
const manifest = await client.getManifest();
|
|
39
|
+
if (!manifest)
|
|
40
|
+
return entries;
|
|
41
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
42
|
+
const normalizedPath = basePath.replace(/\/+$/, "");
|
|
43
|
+
// Add the blog list page itself
|
|
44
|
+
entries.push({
|
|
45
|
+
url: `${normalizedBase}${normalizedPath}`,
|
|
46
|
+
lastModified: manifest.updatedAt,
|
|
47
|
+
changeFrequency,
|
|
48
|
+
priority: priority - 0.1,
|
|
49
|
+
});
|
|
50
|
+
// Fetch all list pages to gather every article slug
|
|
51
|
+
const totalPages = manifest.totalPages;
|
|
52
|
+
for (let page = 1; page <= totalPages; page++) {
|
|
53
|
+
const listPage = await client.getListPage(page);
|
|
54
|
+
if (!listPage)
|
|
55
|
+
continue;
|
|
56
|
+
for (const article of listPage.articles) {
|
|
57
|
+
entries.push({
|
|
58
|
+
url: `${normalizedBase}${normalizedPath}/${article.slug}`,
|
|
59
|
+
lastModified: article.createdAt,
|
|
60
|
+
changeFrequency,
|
|
61
|
+
priority,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return entries;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a Next.js App Router sitemap function that returns blog article entries.
|
|
69
|
+
* Use as the default export of `app/blog/sitemap.ts` or merge into `app/sitemap.ts`.
|
|
70
|
+
*/
|
|
71
|
+
export function createBlogSitemap(options) {
|
|
72
|
+
return async function sitemap() {
|
|
73
|
+
return getBlogSitemapEntries(options);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=sitemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../../src/next/sitemap.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,0CAA0C;AAC1C,gFAAgF;AAChF,6DAA6D;AAC7D,sDAAsD;AACtD,EAAE;AACF,SAAS;AACT,gDAAgD;AAChD,6DAA6D;AAC7D,EAAE;AACF,kDAAkD;AAClD,uFAAuF;AACvF,gCAAgC;AAChC,EAAE;AACF,+CAA+C;AAC/C,iEAAiE;AACjE,EAAE;AACF,8CAA8C;AAC9C,wDAAwD;AACxD,+CAA+C;AAC/C,wCAAwC;AACxC,UAAU;AACV,eAAe;AACf,kEAAkE;AAClE,wBAAwB;AACxB,SAAS;AACT,MAAM;AACN,gFAAgF;AAEhF,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAqC3C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAA2B;IAE3B,MAAM,EACJ,MAAM,EACN,UAAU,EACV,OAAO,EACP,QAAQ,GAAG,OAAO,EAClB,eAAe,GAAG,QAAQ,EAC1B,QAAQ,GAAG,GAAG,EACd,SAAS,GACV,GAAG,OAAO,CAAC;IAEZ,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC;IACrE,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IAC5C,IAAI,CAAC,QAAQ;QAAE,OAAO,OAAO,CAAC;IAE9B,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEpD,gCAAgC;IAChC,OAAO,CAAC,IAAI,CAAC;QACX,GAAG,EAAE,GAAG,cAAc,GAAG,cAAc,EAAE;QACzC,YAAY,EAAE,QAAQ,CAAC,SAAS;QAChC,eAAe;QACf,QAAQ,EAAE,QAAQ,GAAG,GAAG;KACzB,CAAC,CAAC;IAEH,oDAAoD;IACpD,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IACvC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC;gBACX,GAAG,EAAE,GAAG,cAAc,GAAG,cAAc,IAAI,OAAO,CAAC,IAAI,EAAE;gBACzD,YAAY,EAAE,OAAO,CAAC,SAAS;gBAC/B,eAAe;gBACf,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,OAAO,KAAK,UAAU,OAAO;QAC3B,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@earlyseo/blog",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Drop-in blog integration for React & Next.js — powered by EarlySEO. Articles are served from EarlySEO's CDN. Just add your site ID and render.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,25 +13,21 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"import": "./dist/index.js",
|
|
16
|
-
"require": "./dist/index.js",
|
|
17
16
|
"default": "./dist/index.js"
|
|
18
17
|
},
|
|
19
18
|
"./react": {
|
|
20
19
|
"types": "./dist/react/index.d.ts",
|
|
21
20
|
"import": "./dist/react/index.js",
|
|
22
|
-
"require": "./dist/react/index.js",
|
|
23
21
|
"default": "./dist/react/index.js"
|
|
24
22
|
},
|
|
25
23
|
"./next": {
|
|
26
24
|
"types": "./dist/next/index.d.ts",
|
|
27
25
|
"import": "./dist/next/index.js",
|
|
28
|
-
"require": "./dist/next/index.js",
|
|
29
26
|
"default": "./dist/next/index.js"
|
|
30
27
|
},
|
|
31
28
|
"./css": {
|
|
32
29
|
"types": "./dist/css.d.ts",
|
|
33
30
|
"import": "./dist/css.js",
|
|
34
|
-
"require": "./dist/css.js",
|
|
35
31
|
"default": "./dist/css.js"
|
|
36
32
|
}
|
|
37
33
|
},
|
package/src/cli/init.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import fs from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import readline from "node:readline";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
12
13
|
|
|
13
14
|
const cwd = process.cwd();
|
|
14
15
|
|
|
@@ -99,6 +100,27 @@ export const generateMetadata = createBlogPostMetadata({
|
|
|
99
100
|
`;
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
function blogSitemapPage(siteId) {
|
|
104
|
+
return `import { getBlogSitemapEntries } from "@earlyseo/blog/next";
|
|
105
|
+
import type { MetadataRoute } from "next";
|
|
106
|
+
|
|
107
|
+
const siteId = process.env.EARLYSEO_SITE_ID ?? "${siteId}";
|
|
108
|
+
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? "https://example.com";
|
|
109
|
+
|
|
110
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
111
|
+
const blogEntries = await getBlogSitemapEntries({
|
|
112
|
+
siteId,
|
|
113
|
+
baseUrl,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return [
|
|
117
|
+
{ url: baseUrl, lastModified: new Date() },
|
|
118
|
+
...blogEntries,
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
|
|
102
124
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
103
125
|
|
|
104
126
|
async function main() {
|
|
@@ -147,6 +169,27 @@ async function main() {
|
|
|
147
169
|
|
|
148
170
|
console.log();
|
|
149
171
|
|
|
172
|
+
// 4. Install @earlyseo/blog as a project dependency
|
|
173
|
+
const pkgJsonPath = path.join(cwd, "package.json");
|
|
174
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
175
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
176
|
+
const deps = pkgJson.dependencies || {};
|
|
177
|
+
const alreadyInstalled = "@earlyseo/blog" in deps;
|
|
178
|
+
if (!alreadyInstalled) {
|
|
179
|
+
console.log(" 📦 Installing @earlyseo/blog...");
|
|
180
|
+
try {
|
|
181
|
+
execSync("npm install @earlyseo/blog@latest", { cwd, stdio: "ignore" });
|
|
182
|
+
console.log(" ✅ Installed @earlyseo/blog");
|
|
183
|
+
} catch {
|
|
184
|
+
console.log(" ⚠ Could not auto-install. Run manually: npm install @earlyseo/blog");
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
console.log(" ✓ @earlyseo/blog already in dependencies");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log();
|
|
192
|
+
|
|
150
193
|
// 5. Generate pages
|
|
151
194
|
const blogDir = `${appDir}/blog`;
|
|
152
195
|
const listPagePath = `${blogDir}/page.tsx`;
|
|
@@ -155,9 +198,14 @@ async function main() {
|
|
|
155
198
|
await writeFileSafe(listPagePath, blogListPage(siteId));
|
|
156
199
|
await writeFileSafe(postPagePath, blogPostPage(siteId));
|
|
157
200
|
|
|
158
|
-
//
|
|
201
|
+
// 5b. Generate sitemap (at app root)
|
|
202
|
+
const sitemapPath = `${appDir}/sitemap.ts`;
|
|
203
|
+
await writeFileSafe(sitemapPath, blogSitemapPage(siteId));
|
|
204
|
+
|
|
205
|
+
// 6. Add EARLYSEO_SITE_ID and NEXT_PUBLIC_BASE_URL to .env.local
|
|
159
206
|
const envPath = path.join(cwd, ".env.local");
|
|
160
207
|
const envKey = "EARLYSEO_SITE_ID";
|
|
208
|
+
const baseUrlKey = "NEXT_PUBLIC_BASE_URL";
|
|
161
209
|
if (fs.existsSync(envPath)) {
|
|
162
210
|
const envContent = fs.readFileSync(envPath, "utf-8");
|
|
163
211
|
if (envContent.includes(envKey)) {
|
|
@@ -166,9 +214,13 @@ async function main() {
|
|
|
166
214
|
fs.appendFileSync(envPath, `\n# EarlySEO Blog SDK\n${envKey}=${siteId}\n`);
|
|
167
215
|
console.log(` ✅ Added ${envKey} to .env.local`);
|
|
168
216
|
}
|
|
217
|
+
if (!envContent.includes(baseUrlKey)) {
|
|
218
|
+
fs.appendFileSync(envPath, `${baseUrlKey}=https://example.com\n`);
|
|
219
|
+
console.log(` ✅ Added ${baseUrlKey} to .env.local (update with your site URL)`);
|
|
220
|
+
}
|
|
169
221
|
} else {
|
|
170
|
-
fs.writeFileSync(envPath, `# EarlySEO Blog SDK\n${envKey}=${siteId}\n`, "utf-8");
|
|
171
|
-
console.log(` ✅ Created .env.local with ${envKey}`);
|
|
222
|
+
fs.writeFileSync(envPath, `# EarlySEO Blog SDK\n${envKey}=${siteId}\n${baseUrlKey}=https://example.com\n`, "utf-8");
|
|
223
|
+
console.log(` ✅ Created .env.local with ${envKey} and ${baseUrlKey}`);
|
|
172
224
|
}
|
|
173
225
|
|
|
174
226
|
// 7. Done
|
|
@@ -177,9 +229,11 @@ async function main() {
|
|
|
177
229
|
console.log(" ✓ Setup complete! Your blog is ready at /blog");
|
|
178
230
|
console.log();
|
|
179
231
|
console.log(" Next steps:");
|
|
180
|
-
console.log(" 1.
|
|
181
|
-
console.log(" 2.
|
|
182
|
-
console.log(" 3.
|
|
232
|
+
console.log(" 1. Update NEXT_PUBLIC_BASE_URL in .env.local with your site URL");
|
|
233
|
+
console.log(" 2. Start your dev server: npm run dev");
|
|
234
|
+
console.log(" 3. Visit: http://localhost:3000/blog");
|
|
235
|
+
console.log(" 4. Sitemap available at: http://localhost:3000/sitemap.xml");
|
|
236
|
+
console.log(" 5. Publish articles from your EarlySEO dashboard");
|
|
183
237
|
console.log();
|
|
184
238
|
console.log(" Docs: https://www.npmjs.com/package/@earlyseo/blog");
|
|
185
239
|
console.log();
|