aeo.js 0.0.7 → 0.0.9

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 (68) hide show
  1. package/README.md +119 -164
  2. package/dist/angular.d.mts +1 -1
  3. package/dist/angular.d.ts +1 -1
  4. package/dist/angular.js +69 -13
  5. package/dist/angular.js.map +1 -1
  6. package/dist/angular.mjs +69 -13
  7. package/dist/angular.mjs.map +1 -1
  8. package/dist/astro.d.mts +1 -1
  9. package/dist/astro.d.ts +1 -1
  10. package/dist/astro.js +78 -18
  11. package/dist/astro.js.map +1 -1
  12. package/dist/astro.mjs +78 -18
  13. package/dist/astro.mjs.map +1 -1
  14. package/dist/cli.js +84 -27
  15. package/dist/cli.js.map +1 -1
  16. package/dist/cli.mjs +84 -27
  17. package/dist/cli.mjs.map +1 -1
  18. package/dist/index.d.mts +2 -2
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +89 -29
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +89 -29
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/next.d.mts +1 -1
  25. package/dist/next.d.ts +1 -1
  26. package/dist/next.js +71 -15
  27. package/dist/next.js.map +1 -1
  28. package/dist/next.mjs +71 -15
  29. package/dist/next.mjs.map +1 -1
  30. package/dist/nuxt.d.mts +1 -1
  31. package/dist/nuxt.d.ts +1 -1
  32. package/dist/nuxt.js +69 -13
  33. package/dist/nuxt.js.map +1 -1
  34. package/dist/nuxt.mjs +69 -13
  35. package/dist/nuxt.mjs.map +1 -1
  36. package/dist/react.d.mts +1 -1
  37. package/dist/react.d.ts +1 -1
  38. package/dist/react.js +52 -7
  39. package/dist/react.js.map +1 -1
  40. package/dist/react.mjs +52 -7
  41. package/dist/react.mjs.map +1 -1
  42. package/dist/{types-Cn_Qbkmg.d.mts → types-BlLNcw-X.d.mts} +2 -0
  43. package/dist/{types-Cn_Qbkmg.d.ts → types-BlLNcw-X.d.ts} +2 -0
  44. package/dist/vite.d.mts +1 -1
  45. package/dist/vite.d.ts +1 -1
  46. package/dist/vite.js +94 -20
  47. package/dist/vite.js.map +1 -1
  48. package/dist/vite.mjs +94 -20
  49. package/dist/vite.mjs.map +1 -1
  50. package/dist/vue.d.mts +1 -1
  51. package/dist/vue.d.ts +1 -1
  52. package/dist/vue.js +52 -7
  53. package/dist/vue.js.map +1 -1
  54. package/dist/vue.mjs +52 -7
  55. package/dist/vue.mjs.map +1 -1
  56. package/dist/webpack.d.mts +1 -1
  57. package/dist/webpack.d.ts +1 -1
  58. package/dist/webpack.js +69 -13
  59. package/dist/webpack.js.map +1 -1
  60. package/dist/webpack.mjs +69 -13
  61. package/dist/webpack.mjs.map +1 -1
  62. package/dist/widget.d.mts +1 -1
  63. package/dist/widget.d.ts +1 -1
  64. package/dist/widget.js +52 -7
  65. package/dist/widget.js.map +1 -1
  66. package/dist/widget.mjs +52 -7
  67. package/dist/widget.mjs.map +1 -1
  68. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -78,10 +78,19 @@ function generateRobotsTxt(config) {
78
78
  }
79
79
  lines.push("# Default for all other bots");
80
80
  lines.push("User-agent: *");
81
- lines.push("Allow: /");
81
+ for (const path of config.robots.allow.length > 0 ? config.robots.allow : ["/"]) {
82
+ lines.push(`Allow: ${path}`);
83
+ }
84
+ for (const path of config.robots.disallow) {
85
+ lines.push(`Disallow: ${path}`);
86
+ }
87
+ if (config.robots.crawlDelay > 0) {
88
+ lines.push(`Crawl-delay: ${config.robots.crawlDelay}`);
89
+ }
82
90
  lines.push("");
83
- if (config.url) {
84
- lines.push(`Sitemap: ${config.url}/sitemap.xml`);
91
+ const sitemapUrl = config.robots.sitemap || (config.url ? `${config.url}/sitemap.xml` : "");
92
+ if (sitemapUrl) {
93
+ lines.push(`Sitemap: ${sitemapUrl}`);
85
94
  }
86
95
  lines.push("");
87
96
  lines.push("# AEO (Answer Engine Optimization) files");
@@ -163,7 +172,7 @@ function detectFramework(projectRoot = process.cwd()) {
163
172
  };
164
173
  }
165
174
  function resolveConfig(config = {}) {
166
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M;
175
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N;
167
176
  const frameworkInfo = detectFramework();
168
177
  return {
169
178
  title: config.title || "My Site",
@@ -207,15 +216,16 @@ function resolveConfig(config = {}) {
207
216
  widget: {
208
217
  enabled: ((_A = config.widget) == null ? void 0 : _A.enabled) !== false,
209
218
  position: ((_B = config.widget) == null ? void 0 : _B.position) || "bottom-right",
219
+ size: ((_C = config.widget) == null ? void 0 : _C.size) || "default",
210
220
  theme: {
211
- background: ((_D = (_C = config.widget) == null ? void 0 : _C.theme) == null ? void 0 : _D.background) || "rgba(18, 18, 24, 0.9)",
212
- text: ((_F = (_E = config.widget) == null ? void 0 : _E.theme) == null ? void 0 : _F.text) || "#C0C0C5",
213
- accent: ((_H = (_G = config.widget) == null ? void 0 : _G.theme) == null ? void 0 : _H.accent) || "#E8E8EA",
214
- badge: ((_J = (_I = config.widget) == null ? void 0 : _I.theme) == null ? void 0 : _J.badge) || "#4ADE80"
221
+ background: ((_E = (_D = config.widget) == null ? void 0 : _D.theme) == null ? void 0 : _E.background) || "rgba(18, 18, 24, 0.9)",
222
+ text: ((_G = (_F = config.widget) == null ? void 0 : _F.theme) == null ? void 0 : _G.text) || "#C0C0C5",
223
+ accent: ((_I = (_H = config.widget) == null ? void 0 : _H.theme) == null ? void 0 : _I.accent) || "#E8E8EA",
224
+ badge: ((_K = (_J = config.widget) == null ? void 0 : _J.theme) == null ? void 0 : _K.badge) || "#4ADE80"
215
225
  },
216
- humanLabel: ((_K = config.widget) == null ? void 0 : _K.humanLabel) || "Human",
217
- aiLabel: ((_L = config.widget) == null ? void 0 : _L.aiLabel) || "AI",
218
- showBadge: ((_M = config.widget) == null ? void 0 : _M.showBadge) !== false
226
+ humanLabel: ((_L = config.widget) == null ? void 0 : _L.humanLabel) || "Human",
227
+ aiLabel: ((_M = config.widget) == null ? void 0 : _M.aiLabel) || "AI",
228
+ showBadge: ((_N = config.widget) == null ? void 0 : _N.showBadge) !== false
219
229
  }
220
230
  };
221
231
  }
@@ -272,7 +282,8 @@ function collectMarkdownFiles(dir, base = dir) {
272
282
  for (const entry of entries) {
273
283
  const fullPath = join(dir, entry);
274
284
  const stat = statSync(fullPath);
275
- if (stat.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
285
+ const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "public", "dist", ".next", ".nuxt", ".output", ".open-next", "coverage", "__tests__", "__mocks__"]);
286
+ if (stat.isDirectory() && !entry.startsWith(".") && !SKIP_DIRS.has(entry)) {
276
287
  files.push(...collectMarkdownFiles(fullPath, base));
277
288
  } else if (stat.isFile() && (extname(entry) === ".md" || extname(entry) === ".mdx")) {
278
289
  const content = readFileSync(fullPath, "utf-8");
@@ -615,7 +626,8 @@ function collectUrls(dir, config, base = dir) {
615
626
  for (const entry of entries) {
616
627
  const fullPath = join(dir, entry);
617
628
  const stat = statSync(fullPath);
618
- if (stat.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
629
+ const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "public", "dist", ".next", ".nuxt", ".output", ".open-next", "coverage", "__tests__", "__mocks__"]);
630
+ if (stat.isDirectory() && !entry.startsWith(".") && !SKIP_DIRS.has(entry)) {
619
631
  urls.push(...collectUrls(fullPath, config, base));
620
632
  } else if (stat.isFile() && (extname(entry) === ".md" || extname(entry) === ".mdx" || extname(entry) === ".html")) {
621
633
  const relativePath = relative(base, fullPath);
@@ -843,6 +855,22 @@ function generatePageSchemas(page, config) {
843
855
  }))
844
856
  });
845
857
  }
858
+ if (faqItems.length === 0) {
859
+ const howToSteps = detectHowToSteps(page.content || "");
860
+ if (howToSteps.length > 0) {
861
+ schemas.push({
862
+ "@context": "https://schema.org",
863
+ "@type": "HowTo",
864
+ name: page.title || config.title,
865
+ step: howToSteps.map((s, i) => ({
866
+ "@type": "HowToStep",
867
+ position: i + 1,
868
+ name: s.name,
869
+ text: s.text
870
+ }))
871
+ });
872
+ }
873
+ }
846
874
  const pageType = config.schema.defaultType;
847
875
  const pageSchema = {
848
876
  "@context": "https://schema.org",
@@ -919,6 +947,34 @@ function detectFaqPatterns(content) {
919
947
  }
920
948
  return items;
921
949
  }
950
+ function detectHowToSteps(content) {
951
+ const steps = [];
952
+ const lines = content.split("\n");
953
+ for (let i = 0; i < lines.length; i++) {
954
+ const line = lines[i].trim();
955
+ const stepMatch = line.match(/^#{1,6}\s+(?:Step\s+\d+[\s:.-]*|(\d+)[.)]\s*)(.+)$/i);
956
+ if (stepMatch) {
957
+ const name = (stepMatch[2] || stepMatch[1] || "").trim();
958
+ const bodyLines = [];
959
+ for (let j = i + 1; j < lines.length; j++) {
960
+ const nextLine = lines[j].trim();
961
+ if (!nextLine) {
962
+ if (bodyLines.length > 0) break;
963
+ continue;
964
+ }
965
+ if (/^#{1,6}\s/.test(nextLine)) break;
966
+ bodyLines.push(nextLine);
967
+ }
968
+ if (name) {
969
+ steps.push({
970
+ name,
971
+ text: bodyLines.join(" ").slice(0, 500)
972
+ });
973
+ }
974
+ }
975
+ }
976
+ return steps.length >= 2 ? steps : [];
977
+ }
922
978
  async function generateAEOFiles(configOrRoot, maybeConfig) {
923
979
  var _a;
924
980
  let config;
@@ -1128,15 +1184,18 @@ function auditSchemaPresence(config, issues, suggestions) {
1128
1184
  if (!hasLogo) {
1129
1185
  suggestions.push("Add schema.organization.logo for richer search results and AI knowledge");
1130
1186
  }
1131
- const hasSameAs = config.schema.organization.sameAs.length > 0;
1132
- checks.push({ label: "Social profiles linked (sameAs)", passed: hasSameAs, points: hasSameAs ? 4 : 0 });
1133
- if (!hasSameAs) {
1134
- issues.push({ category: "Schema Presence", severity: "warning", message: "No social profiles (sameAs) \u2014 critical for GEO/E-E-A-T signals", fix: "Add schema.organization.sameAs with social profile URLs" });
1187
+ const hasFaqOrHowTo = config.pages.some((p) => {
1188
+ const content = p.content || "";
1189
+ return /^#{1,6}\s+.+\?\s*$/m.test(content) || /^#{1,6}\s+(?:Step\s+\d+[\s:.-]|How\s+to)/im.test(content);
1190
+ });
1191
+ checks.push({ label: "FAQPage or HowTo schema", passed: hasFaqOrHowTo, points: hasFaqOrHowTo ? 4 : 0 });
1192
+ if (!hasFaqOrHowTo) {
1193
+ suggestions.push("Add FAQ sections (question headings) or step-by-step content to auto-generate FAQPage/HowTo schema");
1135
1194
  }
1136
- const hasRealUrl = config.url !== "https://example.com" && config.url !== "";
1137
- checks.push({ label: "Site URL is configured (not default)", passed: hasRealUrl, points: hasRealUrl ? 4 : 0 });
1138
- if (!hasRealUrl) {
1139
- issues.push({ category: "Schema Presence", severity: "error", message: "Site URL is still the default (https://example.com)", fix: "Set url to your actual site URL" });
1195
+ const hasArticleOrWebPage = schemaEnabled && (config.schema.defaultType === "Article" || config.schema.defaultType === "WebPage");
1196
+ checks.push({ label: "Article/WebPage schema", passed: hasArticleOrWebPage, points: hasArticleOrWebPage ? 4 : 0 });
1197
+ if (!hasArticleOrWebPage && schemaEnabled) {
1198
+ suggestions.push('Set schema.defaultType to "Article" or "WebPage" for per-page structured data');
1140
1199
  }
1141
1200
  return {
1142
1201
  name: "Schema Presence",
@@ -1175,12 +1234,10 @@ function auditMetaQuality(config, issues, suggestions) {
1175
1234
  if (!goodTitleCoverage && config.pages.length > 0) {
1176
1235
  issues.push({ category: "Meta Quality", severity: "warning", message: `Only ${pagesWithTitles.length}/${config.pages.length} pages have titles`, fix: "Add titles to all pages" });
1177
1236
  }
1178
- const pagesWithDesc = config.pages.filter((p) => p.description && p.description.length > 0);
1179
- const descCoverage = config.pages.length > 0 ? pagesWithDesc.length / config.pages.length : 0;
1180
- const goodDescCoverage = descCoverage >= 0.5;
1181
- checks.push({ label: "50%+ of pages have descriptions", passed: goodDescCoverage, points: goodDescCoverage ? 4 : 0 });
1182
- if (!goodDescCoverage && config.pages.length > 0) {
1183
- suggestions.push(`Only ${pagesWithDesc.length}/${config.pages.length} pages have descriptions \u2014 add per-page descriptions`);
1237
+ const hasOgImage = !!config.og.image;
1238
+ checks.push({ label: "OG image configured", passed: hasOgImage, points: hasOgImage ? 4 : 0 });
1239
+ if (!hasOgImage) {
1240
+ suggestions.push("Set og.image for richer social sharing previews and AI citation cards");
1184
1241
  }
1185
1242
  return {
1186
1243
  name: "Meta Quality",