@webnumseoagent/next 0.7.0 → 0.7.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/bin/cli.mjs CHANGED
@@ -712,9 +712,12 @@ async function blogPage() {
712
712
  : "{ params }";
713
713
  const localeArg = param ? `, { locale: params.${param} }` : "";
714
714
  const localeProp = param ? ` locale={params.${param}}` : "";
715
+ // Мультиязычный роут /[locale]/blog/[slug]: пре-рендерим КАЖДУЮ статью под КАЖДУЮ локаль (передаём имя
716
+ // сегмента локали) — иначе force-static не покроет комбинацию locale×slug и статьи отдадут 404.
717
+ const gsp = param ? `seoBlogStaticParams({ localeParam: "${param}" })` : `seoBlogStaticParams()`;
715
718
  await writeOurRoute(
716
719
  path.join(root, appDir, under, `blog/[slug]/page.${jsxExt}`),
717
- `import { notFound } from "next/navigation";\nimport { seoBlogPost, seoBlogStaticParams, SeoBlogArticle } from "${blogFor(`${under}blog/[slug]/page.tsx`)}";\nexport const dynamic = "force-static";\nexport const dynamicParams = false;\n\n// Полностью статический рендер: все статьи пре-рендерятся на сборке из файлов content/blog. Контент\n// меняется git-коммитом → редеплой. dynamicParams=false: неизвестный слаг → 404 (без рантайм-чтения).\nexport async function generateStaticParams() {\n return seoBlogStaticParams();\n}\n\nexport async function generateMetadata(${artSig}) {\n const post = await seoBlogPost(params.slug${localeArg});\n if (!post) return {};\n return { title: post.title, description: post.excerpt, openGraph: { images: post.cover ? [post.cover] : [] } };\n}\n\nexport default async function BlogPostPage(${artSig}) {\n const post = await seoBlogPost(params.slug${localeArg});\n if (!post) notFound();\n return <SeoBlogArticle post={post}${localeProp} />;\n}\n`,
720
+ `import { notFound } from "next/navigation";\nimport { seoBlogPost, seoBlogStaticParams, SeoBlogArticle } from "${blogFor(`${under}blog/[slug]/page.tsx`)}";\nexport const dynamic = "force-static";\nexport const dynamicParams = false;\n\n// Полностью статический рендер: все статьи пре-рендерятся на сборке из файлов content/blog. Контент\n// меняется git-коммитом → редеплой. dynamicParams=false: неизвестный слаг → 404 (без рантайм-чтения).\nexport async function generateStaticParams() {\n return ${gsp};\n}\n\nexport async function generateMetadata(${artSig}) {\n const post = await seoBlogPost(params.slug${localeArg});\n if (!post) return {};\n return { title: post.title, description: post.excerpt, openGraph: { images: post.cover ? [post.cover] : [] } };\n}\n\nexport default async function BlogPostPage(${artSig}) {\n const post = await seoBlogPost(params.slug${localeArg});\n if (!post) notFound();\n return <SeoBlogArticle post={post}${localeProp} />;\n}\n`,
718
721
  );
719
722
  }
720
723
 
package/blog-fs.ts CHANGED
@@ -65,6 +65,13 @@ export function fsDefaultLocale(): string {
65
65
  return typeof d === "string" ? d : ""; // битый дескриптор мог дать не-строку → не роняем split ниже
66
66
  }
67
67
 
68
+ // Локали доставки блога из дескриптора (для generateStaticParams мультиязычного роута /[locale]/blog/[slug]:
69
+ // каждую статью надо пре-рендерить под каждую локаль, иначе force-static отдаст 404). Пусто — одноязычный.
70
+ export function fsLocales(): string[] {
71
+ const l = readDescriptor()?.locales;
72
+ return Array.isArray(l) ? l.map((x) => String(x).trim()).filter(Boolean) : [];
73
+ }
74
+
68
75
  // Собрать meta.json всех статей (glob по папкам). Пропускает битые/незнакомые версии/черновики.
69
76
  function listMetas(): { dir: string; meta: Meta }[] {
70
77
  const r = root();
package/blog.tsx CHANGED
@@ -33,10 +33,18 @@ export async function seoBlogPost(slug: string, opts?: { locale?: string }): Pro
33
33
  return m.fsBlogPost(slug, opts?.locale ?? m.fsDefaultLocale());
34
34
  }
35
35
 
36
- // Параметры для generateStaticParams роута /blog/[slug]: пре-рендер ВСЕХ статей на сборке (SSG из файлов).
37
- export async function seoBlogStaticParams(opts?: { locale?: string }): Promise<{ slug: string }[]> {
38
- const list = await seoBlogList({ locale: opts?.locale, limit: 100000 });
39
- return list.map((p) => ({ slug: p.slug }));
36
+ // Параметры для generateStaticParams роута блога: пре-рендер ВСЕХ статей на сборке (SSG из файлов).
37
+ // Без localeParam [{slug}] (одноязычный /blog/[slug]). С localeParam → [{<localeParam>, slug}] для КАЖДОЙ
38
+ // локали (мультиязычный /[locale]/blog/[slug]): иначе force-static не покроет комбинацию locale×slug → 404.
39
+ export async function seoBlogStaticParams(opts?: { localeParam?: string }): Promise<Array<Record<string, string>>> {
40
+ const list = await seoBlogList({ limit: 100000 });
41
+ const slugs = list.map((p) => ({ slug: p.slug }));
42
+ const lp = opts?.localeParam;
43
+ if (!lp) return slugs;
44
+ const m = await blogfs();
45
+ const locales = m.fsLocales();
46
+ if (!locales.length) return slugs;
47
+ return locales.flatMap((loc) => slugs.map((s) => ({ [lp]: loc, slug: s.slug })));
40
48
  }
41
49
 
42
50
  // ── Компоненты (серверные) ──────────────────────────────────────────────────────────────────
package/dist/blog-fs.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { SeoBlogSummary, SeoBlogPostFull, SeoBlogTheme } from "./blog";
2
2
  export declare function fsTheme(): SeoBlogTheme | undefined;
3
3
  export declare function fsAvailable(): boolean;
4
4
  export declare function fsDefaultLocale(): string;
5
+ export declare function fsLocales(): string[];
5
6
  export declare function fsBlogList(locale: string, limit: number): SeoBlogSummary[];
6
7
  export declare function fsBlogPost(slug: string, locale: string): SeoBlogPostFull | null;
7
8
  export declare function fsDirExists(): boolean;
package/dist/blog-fs.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fsTheme = fsTheme;
4
4
  exports.fsAvailable = fsAvailable;
5
5
  exports.fsDefaultLocale = fsDefaultLocale;
6
+ exports.fsLocales = fsLocales;
6
7
  exports.fsBlogList = fsBlogList;
7
8
  exports.fsBlogPost = fsBlogPost;
8
9
  exports.fsDirExists = fsDirExists;
@@ -71,6 +72,12 @@ function fsDefaultLocale() {
71
72
  const d = readDescriptor()?.defaultLocale;
72
73
  return typeof d === "string" ? d : ""; // битый дескриптор мог дать не-строку → не роняем split ниже
73
74
  }
75
+ // Локали доставки блога из дескриптора (для generateStaticParams мультиязычного роута /[locale]/blog/[slug]:
76
+ // каждую статью надо пре-рендерить под каждую локаль, иначе force-static отдаст 404). Пусто — одноязычный.
77
+ function fsLocales() {
78
+ const l = readDescriptor()?.locales;
79
+ return Array.isArray(l) ? l.map((x) => String(x).trim()).filter(Boolean) : [];
80
+ }
74
81
  // Собрать meta.json всех статей (glob по папкам). Пропускает битые/незнакомые версии/черновики.
75
82
  function listMetas() {
76
83
  const r = root();
package/dist/blog.d.ts CHANGED
@@ -25,10 +25,8 @@ export declare function seoBlogPost(slug: string, opts?: {
25
25
  locale?: string;
26
26
  }): Promise<SeoBlogPostFull | null>;
27
27
  export declare function seoBlogStaticParams(opts?: {
28
- locale?: string;
29
- }): Promise<{
30
- slug: string;
31
- }[]>;
28
+ localeParam?: string;
29
+ }): Promise<Array<Record<string, string>>>;
32
30
  export declare function SeoBlogIndex(props: {
33
31
  locale?: string;
34
32
  basePath?: string;
package/dist/blog.js CHANGED
@@ -50,10 +50,20 @@ async function seoBlogPost(slug, opts) {
50
50
  const m = await blogfs();
51
51
  return m.fsBlogPost(slug, opts?.locale ?? m.fsDefaultLocale());
52
52
  }
53
- // Параметры для generateStaticParams роута /blog/[slug]: пре-рендер ВСЕХ статей на сборке (SSG из файлов).
53
+ // Параметры для generateStaticParams роута блога: пре-рендер ВСЕХ статей на сборке (SSG из файлов).
54
+ // Без localeParam → [{slug}] (одноязычный /blog/[slug]). С localeParam → [{<localeParam>, slug}] для КАЖДОЙ
55
+ // локали (мультиязычный /[locale]/blog/[slug]): иначе force-static не покроет комбинацию locale×slug → 404.
54
56
  async function seoBlogStaticParams(opts) {
55
- const list = await seoBlogList({ locale: opts?.locale, limit: 100000 });
56
- return list.map((p) => ({ slug: p.slug }));
57
+ const list = await seoBlogList({ limit: 100000 });
58
+ const slugs = list.map((p) => ({ slug: p.slug }));
59
+ const lp = opts?.localeParam;
60
+ if (!lp)
61
+ return slugs;
62
+ const m = await blogfs();
63
+ const locales = m.fsLocales();
64
+ if (!locales.length)
65
+ return slugs;
66
+ return locales.flatMap((loc) => slugs.map((s) => ({ [lp]: loc, slug: s.slug })));
57
67
  }
58
68
  // ── Компоненты (серверные) ──────────────────────────────────────────────────────────────────
59
69
  // Цвета/рамки/скругления — через СОБСТВЕННЫЕ переменные пакета (--sa-*) с нейтральным дефолтом.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webnumseoagent/next",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Runtime SEO adapter + installer for Next.js App Router sites (SEO Agent platform)",
5
5
  "main": "dist/client.js",
6
6
  "types": "dist/client.d.ts",