@webnumseoagent/next 0.7.2 → 0.7.3

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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/bin/cli.mjs +54 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -105,6 +105,10 @@ Vercel и `git push`. **Проверь `git diff`** перед коммитом
105
105
  контент = git-коммит → редеплой пересобирает страницы.
106
106
  - Тема оформления (фирменный цвет) и канонический URL-префикс берутся из `content/blog/.blogfs.json`.
107
107
  - Sitemap блога добавляется автоматически (раздел `sitemap/blog.xml`).
108
+ - `init`/`blog` **сами** прописывают в `next.config` `outputFileTracingIncludes` для `content/blog` —
109
+ без этого на Vercel файлы блога не попадают в serverless-бандл, и рантайм-чтение (ISR-ревалидация
110
+ страниц + route-handler карты сайта) находит пусто → блог исчезает с сайта и из sitemap. Идемпотентно;
111
+ запускается и на авто-обновлении адаптера, поэтому сайт чинится сам, без правок разработчика.
108
112
 
109
113
  ## Гарантии
110
114
  - **Fail-safe:** при недоступности платформы адаптер возвращает твои дефолты и
package/bin/cli.mjs CHANGED
@@ -95,6 +95,54 @@ async function detectLocales() {
95
95
  return [];
96
96
  }
97
97
 
98
+ // Вставляет ключ в объектный литерал конфига Next (после его открывающей "{"). Возвращает
99
+ // новый текст или null, если знакомую форму конфига не нашли (тогда печатаем ручную инструкцию).
100
+ function injectIntoConfigObject(src, insert) {
101
+ const patterns = [
102
+ /(\bconst\s+nextConfig\b[^=]*=\s*)\{/,
103
+ /(\bconst\s+config\b[^=]*=\s*)\{/,
104
+ /(\bmodule\.exports\s*=\s*)\{/,
105
+ /(\bexport\s+default\s+)\{/,
106
+ ];
107
+ for (const re of patterns) {
108
+ const m = src.match(re);
109
+ if (m) {
110
+ const braceIdx = m.index + m[0].length - 1; // позиция "{"
111
+ return src.slice(0, braceIdx + 1) + insert + src.slice(braceIdx + 1);
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+
117
+ // Гарантирует, что папка блога попадёт в serverless-бандл Vercel (Next `outputFileTracingIncludes`).
118
+ // ЗАЧЕМ: блог читает content/blog через node:fs. На Vercel это надёжно только на СБОРКЕ. Но в РАНТАЙМЕ
119
+ // файлы читают и ISR-ревалидация страниц блога, и route-handler карты сайта (/sitemap/<раздел>.xml —
120
+ // он всегда рантайм). Трассировщик Next не видит динамический readdirSync(cwd + env) → content/blog не
121
+ // кладётся в лямбду → в рантайме там пусто → статьи схлопываются в 404/«нет статей», блога нет в sitemap.
122
+ // Этот ключ форсит включение content/blog в бандл функций. Идемпотентно; best-effort (никогда не роняет).
123
+ // Живёт в CLI (а не в разовой инструкции), чтобы self-update (`npx … blog --force`) авто-чинил ЛЮБОЙ сайт
124
+ // без участия разработчика сайта.
125
+ async function ensureContentTracing(contentDir = "content/blog") {
126
+ const glob = `./${contentDir.replace(/^\.?\/+/, "").replace(/\/+$/, "")}/**/*`;
127
+ const names = ["next.config.ts", "next.config.mjs", "next.config.js", "next.config.cjs"];
128
+ let cfgPath = null;
129
+ for (const n of names) { if (await exists(path.join(root, n))) { cfgPath = path.join(root, n); break; } }
130
+ const manual = `\n \x1b[33mℹ\x1b[0m Добавь в next.config, чтобы блог работал на Vercel (ISR + карта сайта читают файлы в рантайме):\n \x1b[36moutputFileTracingIncludes: { "/**": ["${glob}"] }\x1b[0m`;
131
+ if (!cfgPath) { log(manual); return; }
132
+ const rel = path.relative(root, cfgPath);
133
+ let src = await fs.readFile(cfgPath, "utf8");
134
+ if (/outputFileTracingIncludes/.test(src)) {
135
+ if (src.includes(glob) || /content\/blog/.test(src)) { skip(`${rel} — трассировка блога уже настроена`); return; }
136
+ log(`\n \x1b[33mℹ\x1b[0m В ${rel} уже есть outputFileTracingIncludes — добавь в него ключ "${glob}".`);
137
+ return;
138
+ }
139
+ const insert = `\n // SEO Agent: включаем файлы блога (content/blog) в serverless-бандл — нужно для ISR-ревалидации\n // страниц блога и route-handler карты сайта, которые читают эти файлы в рантайме на Vercel.\n outputFileTracingIncludes: {\n "/**": ["${glob}"],\n },`;
140
+ const patched = injectIntoConfigObject(src, insert);
141
+ if (!patched) { log(`\n \x1b[33mℹ\x1b[0m Не смог авто-обновить ${rel}.${manual}`); return; }
142
+ await fs.writeFile(cfgPath, patched);
143
+ ok(`${rel} (outputFileTracingIncludes → content/blog в бандле)`);
144
+ }
145
+
98
146
  async function init() {
99
147
  log("\n\x1b[1m@webnumseoagent/next — установка\x1b[0m\n");
100
148
  // Проверяем чистоту git ДО создания файлов (чтобы весь дифф установки был обозрим).
@@ -434,6 +482,8 @@ async function init() {
434
482
  } else {
435
483
  log(`\n \x1b[33mℹ\x1b[0m Блог: у сайта уже есть роуты /blog — не трогаю.`);
436
484
  }
485
+ // Файлы блога должны попасть в serverless-бандл Vercel (иначе рантайм-чтение в лямбде пусто).
486
+ await ensureContentTracing();
437
487
  }
438
488
 
439
489
  log("\n\x1b[1mЗатем:\x1b[0m задай эти же env-переменные в Vercel (Settings → Environment Variables) и сделай git push.\n");
@@ -726,6 +776,10 @@ async function blogPage() {
726
776
  );
727
777
  }
728
778
 
779
+ // Файлы блога должны попасть в serverless-бандл Vercel (ISR-ревалидация + карта сайта читают их в
780
+ // рантайме). Идемпотентно — запускается и на self-update (`npx … blog --force`), авто-чиня сайт.
781
+ await ensureContentTracing();
782
+
729
783
  log(`\n \x1b[32m✓\x1b[0m Готово. Проверь \x1b[36mgit diff\x1b[0m, затем commit + push. Блог: \x1b[1m${localeDir ? `/${param}/blog` : "/blog"}\x1b[0m — дизайн/шапка с сайта, список 4-в-ряд, статический рендер из файлов.\n`);
730
784
  }
731
785
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webnumseoagent/next",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
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",