@earlyseo/blog 1.0.5 → 1.0.7
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 -2
- 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 +34 -0
- package/dist/next/sitemap.d.ts.map +1 -0
- package/dist/next/sitemap.js +100 -0
- package/dist/next/sitemap.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/init.mjs +28 -6
package/README.md
CHANGED
|
@@ -20,12 +20,13 @@ 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
|
+
- `app/blog/sitemap.ts` — Blog sitemap at `/blog/sitemap.xml` (auto-detects base URL from Host header)
|
|
29
30
|
- `.env.local` — `EARLYSEO_SITE_ID` added automatically
|
|
30
31
|
|
|
31
32
|
That's it — run `npm run dev` and visit `/blog`.
|
|
@@ -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,40 @@ 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 blog sitemap
|
|
83
|
+
|
|
84
|
+
The recommended approach — a dedicated blog sitemap at `/blog/sitemap.xml`:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
// app/blog/sitemap.ts
|
|
88
|
+
import { createBlogSitemap } from "@earlyseo/blog/next";
|
|
89
|
+
|
|
90
|
+
const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
91
|
+
|
|
92
|
+
// baseUrl is auto-detected from NEXT_PUBLIC_BASE_URL or the request Host header
|
|
93
|
+
export default createBlogSitemap({ siteId });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Or merge blog entries into your existing `app/sitemap.ts`:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { getBlogSitemapEntries } from "@earlyseo/blog/next";
|
|
100
|
+
import type { MetadataRoute } from "next";
|
|
101
|
+
|
|
102
|
+
const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
103
|
+
|
|
104
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
105
|
+
const blogEntries = await getBlogSitemapEntries({ siteId });
|
|
106
|
+
|
|
107
|
+
return [
|
|
108
|
+
{ url: "https://example.com", lastModified: new Date() },
|
|
109
|
+
...blogEntries,
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`baseUrl` is resolved automatically: explicit option → `NEXT_PUBLIC_BASE_URL` env var → request `Host` header.
|
|
115
|
+
|
|
80
116
|
---
|
|
81
117
|
|
|
82
118
|
## How It Works
|
|
@@ -280,6 +316,15 @@ interface Article {
|
|
|
280
316
|
canonicalUrl?: string;
|
|
281
317
|
version: number;
|
|
282
318
|
}
|
|
319
|
+
|
|
320
|
+
interface BlogSitemapOptions {
|
|
321
|
+
siteId: string;
|
|
322
|
+
baseUrl: string;
|
|
323
|
+
basePath?: string;
|
|
324
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
325
|
+
priority?: number;
|
|
326
|
+
fetchInit?: RequestInit;
|
|
327
|
+
}
|
|
283
328
|
```
|
|
284
329
|
|
|
285
330
|
### Imports
|
|
@@ -288,7 +333,7 @@ interface Article {
|
|
|
288
333
|
|-------------|----------|
|
|
289
334
|
| `@earlyseo/blog` | `EarlySeoClient`, core types, CSS constants |
|
|
290
335
|
| `@earlyseo/blog/react` | Provider, hooks, components |
|
|
291
|
-
| `@earlyseo/blog/next` | Next.js page creators, metadata helpers |
|
|
336
|
+
| `@earlyseo/blog/next` | Next.js page creators, metadata helpers, sitemap helpers |
|
|
292
337
|
| `@earlyseo/blog/css` | `ARTICLE_CSS`, `BLOG_CSS` |
|
|
293
338
|
|
|
294
339
|
---
|
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,34 @@
|
|
|
1
|
+
import type { EarlySeoConfig } from "../types";
|
|
2
|
+
export interface BlogSitemapOptions extends EarlySeoConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Your site's public base URL (e.g. "https://example.com").
|
|
5
|
+
* If omitted, falls back to NEXT_PUBLIC_BASE_URL env var,
|
|
6
|
+
* then to the request Host header (via next/headers).
|
|
7
|
+
*/
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
/** Blog base path (default: "/blog") */
|
|
10
|
+
basePath?: string;
|
|
11
|
+
/** Change frequency hint for sitemap (default: "weekly") */
|
|
12
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
13
|
+
/** Priority hint for blog articles (default: 0.7) */
|
|
14
|
+
priority?: number;
|
|
15
|
+
/** Additional fetch options (e.g. `{ next: { revalidate: 60 } }`) */
|
|
16
|
+
fetchInit?: RequestInit;
|
|
17
|
+
}
|
|
18
|
+
export interface SitemapEntry {
|
|
19
|
+
url: string;
|
|
20
|
+
lastModified?: Date | string;
|
|
21
|
+
changeFrequency?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
22
|
+
priority?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Fetches all published blog articles and returns an array of sitemap entries.
|
|
26
|
+
* Use this when you want to merge blog entries into an existing sitemap.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getBlogSitemapEntries(options: BlogSitemapOptions): Promise<SitemapEntry[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Creates a Next.js App Router sitemap function that returns blog article entries.
|
|
31
|
+
* Use as the default export of `app/blog/sitemap.ts` (serves `/blog/sitemap.xml`).
|
|
32
|
+
*/
|
|
33
|
+
export declare function createBlogSitemap(options: BlogSitemapOptions): () => Promise<SitemapEntry[]>;
|
|
34
|
+
//# sourceMappingURL=sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/next/sitemap.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,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;AA2BD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CA8CzB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,SAC1B,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzD"}
|
|
@@ -0,0 +1,100 @@
|
|
|
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 `app/blog/sitemap.ts` files.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// // app/blog/sitemap.ts → serves /blog/sitemap.xml
|
|
9
|
+
// import { createBlogSitemap } from "@earlyseo/blog/next";
|
|
10
|
+
//
|
|
11
|
+
// const siteId = process.env.EARLYSEO_SITE_ID!;
|
|
12
|
+
// export default createBlogSitemap({ siteId });
|
|
13
|
+
// // baseUrl is auto-detected from NEXT_PUBLIC_BASE_URL or the request host
|
|
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
|
+
// });
|
|
22
|
+
// return [
|
|
23
|
+
// { url: "https://example.com", lastModified: new Date() },
|
|
24
|
+
// ...blogEntries,
|
|
25
|
+
// ];
|
|
26
|
+
// }
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
import { EarlySeoClient } from "../client";
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the base URL from: explicit option → NEXT_PUBLIC_BASE_URL → Host header.
|
|
31
|
+
*/
|
|
32
|
+
async function resolveBaseUrl(explicit) {
|
|
33
|
+
if (explicit)
|
|
34
|
+
return explicit;
|
|
35
|
+
const envUrl = process.env.NEXT_PUBLIC_BASE_URL;
|
|
36
|
+
if (envUrl)
|
|
37
|
+
return envUrl;
|
|
38
|
+
// Fallback: read host from the incoming request via next/headers
|
|
39
|
+
try {
|
|
40
|
+
const { headers } = await import("next/headers");
|
|
41
|
+
const headerStore = await headers();
|
|
42
|
+
const host = headerStore.get("host");
|
|
43
|
+
if (host) {
|
|
44
|
+
const proto = headerStore.get("x-forwarded-proto") ?? "https";
|
|
45
|
+
return `${proto}://${host}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// next/headers unavailable (e.g. build time) — fall through
|
|
50
|
+
}
|
|
51
|
+
return "https://localhost:3000";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fetches all published blog articles and returns an array of sitemap entries.
|
|
55
|
+
* Use this when you want to merge blog entries into an existing sitemap.
|
|
56
|
+
*/
|
|
57
|
+
export async function getBlogSitemapEntries(options) {
|
|
58
|
+
const { siteId, cdnBaseUrl, baseUrl: explicitBaseUrl, basePath = "/blog", changeFrequency = "weekly", priority = 0.7, fetchInit, } = options;
|
|
59
|
+
const baseUrl = await resolveBaseUrl(explicitBaseUrl);
|
|
60
|
+
const client = new EarlySeoClient({ siteId, cdnBaseUrl }, fetchInit);
|
|
61
|
+
const entries = [];
|
|
62
|
+
const manifest = await client.getManifest();
|
|
63
|
+
if (!manifest)
|
|
64
|
+
return entries;
|
|
65
|
+
const normalizedBase = baseUrl.replace(/\/+$/, "");
|
|
66
|
+
const normalizedPath = basePath.replace(/\/+$/, "");
|
|
67
|
+
// Add the blog list page itself
|
|
68
|
+
entries.push({
|
|
69
|
+
url: `${normalizedBase}${normalizedPath}`,
|
|
70
|
+
lastModified: manifest.updatedAt,
|
|
71
|
+
changeFrequency,
|
|
72
|
+
priority: priority - 0.1,
|
|
73
|
+
});
|
|
74
|
+
// Fetch all list pages to gather every article slug
|
|
75
|
+
const totalPages = manifest.totalPages;
|
|
76
|
+
for (let page = 1; page <= totalPages; page++) {
|
|
77
|
+
const listPage = await client.getListPage(page);
|
|
78
|
+
if (!listPage)
|
|
79
|
+
continue;
|
|
80
|
+
for (const article of listPage.articles) {
|
|
81
|
+
entries.push({
|
|
82
|
+
url: `${normalizedBase}${normalizedPath}/${article.slug}`,
|
|
83
|
+
lastModified: article.createdAt,
|
|
84
|
+
changeFrequency,
|
|
85
|
+
priority,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return entries;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a Next.js App Router sitemap function that returns blog article entries.
|
|
93
|
+
* Use as the default export of `app/blog/sitemap.ts` (serves `/blog/sitemap.xml`).
|
|
94
|
+
*/
|
|
95
|
+
export function createBlogSitemap(options) {
|
|
96
|
+
return async function sitemap() {
|
|
97
|
+
return getBlogSitemapEntries(options);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=sitemap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../../src/next/sitemap.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli/init.mjs
CHANGED
|
@@ -100,6 +100,17 @@ export const generateMetadata = createBlogPostMetadata({
|
|
|
100
100
|
`;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function blogSitemapPage(siteId) {
|
|
104
|
+
return `import { createBlogSitemap } from "@earlyseo/blog/next";
|
|
105
|
+
|
|
106
|
+
const siteId = process.env.EARLYSEO_SITE_ID ?? "${siteId}";
|
|
107
|
+
|
|
108
|
+
// baseUrl is auto-detected from NEXT_PUBLIC_BASE_URL or the request Host header.
|
|
109
|
+
// You can also pass it explicitly: createBlogSitemap({ siteId, baseUrl: "https://example.com" })
|
|
110
|
+
export default createBlogSitemap({ siteId });
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
|
|
103
114
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
104
115
|
|
|
105
116
|
async function main() {
|
|
@@ -177,9 +188,14 @@ async function main() {
|
|
|
177
188
|
await writeFileSafe(listPagePath, blogListPage(siteId));
|
|
178
189
|
await writeFileSafe(postPagePath, blogPostPage(siteId));
|
|
179
190
|
|
|
180
|
-
//
|
|
191
|
+
// 5b. Generate blog sitemap (scoped to /blog/sitemap.xml)
|
|
192
|
+
const sitemapPath = `${blogDir}/sitemap.ts`;
|
|
193
|
+
await writeFileSafe(sitemapPath, blogSitemapPage(siteId));
|
|
194
|
+
|
|
195
|
+
// 6. Add EARLYSEO_SITE_ID and NEXT_PUBLIC_BASE_URL to .env.local
|
|
181
196
|
const envPath = path.join(cwd, ".env.local");
|
|
182
197
|
const envKey = "EARLYSEO_SITE_ID";
|
|
198
|
+
const baseUrlKey = "NEXT_PUBLIC_BASE_URL";
|
|
183
199
|
if (fs.existsSync(envPath)) {
|
|
184
200
|
const envContent = fs.readFileSync(envPath, "utf-8");
|
|
185
201
|
if (envContent.includes(envKey)) {
|
|
@@ -188,9 +204,13 @@ async function main() {
|
|
|
188
204
|
fs.appendFileSync(envPath, `\n# EarlySEO Blog SDK\n${envKey}=${siteId}\n`);
|
|
189
205
|
console.log(` ✅ Added ${envKey} to .env.local`);
|
|
190
206
|
}
|
|
207
|
+
if (!envContent.includes(baseUrlKey)) {
|
|
208
|
+
fs.appendFileSync(envPath, `${baseUrlKey}=https://example.com\n`);
|
|
209
|
+
console.log(` ✅ Added ${baseUrlKey} to .env.local (update with your site URL)`);
|
|
210
|
+
}
|
|
191
211
|
} else {
|
|
192
|
-
fs.writeFileSync(envPath, `# EarlySEO Blog SDK\n${envKey}=${siteId}\n`, "utf-8");
|
|
193
|
-
console.log(` ✅ Created .env.local with ${envKey}`);
|
|
212
|
+
fs.writeFileSync(envPath, `# EarlySEO Blog SDK\n${envKey}=${siteId}\n${baseUrlKey}=https://example.com\n`, "utf-8");
|
|
213
|
+
console.log(` ✅ Created .env.local with ${envKey} and ${baseUrlKey}`);
|
|
194
214
|
}
|
|
195
215
|
|
|
196
216
|
// 7. Done
|
|
@@ -199,9 +219,11 @@ async function main() {
|
|
|
199
219
|
console.log(" ✓ Setup complete! Your blog is ready at /blog");
|
|
200
220
|
console.log();
|
|
201
221
|
console.log(" Next steps:");
|
|
202
|
-
console.log(" 1.
|
|
203
|
-
console.log(" 2.
|
|
204
|
-
console.log(" 3.
|
|
222
|
+
console.log(" 1. Optionally set NEXT_PUBLIC_BASE_URL in .env.local (auto-detected from Host if missing)");
|
|
223
|
+
console.log(" 2. Start your dev server: npm run dev");
|
|
224
|
+
console.log(" 3. Visit: http://localhost:3000/blog");
|
|
225
|
+
console.log(" 4. Blog sitemap at: http://localhost:3000/blog/sitemap.xml");
|
|
226
|
+
console.log(" 5. Publish articles from your EarlySEO dashboard");
|
|
205
227
|
console.log();
|
|
206
228
|
console.log(" Docs: https://www.npmjs.com/package/@earlyseo/blog");
|
|
207
229
|
console.log();
|