aeo.js 0.0.2 → 0.0.4
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 +132 -17
- package/dist/angular.d.mts +29 -0
- package/dist/angular.d.ts +29 -0
- package/dist/angular.js +1314 -0
- package/dist/angular.js.map +1 -0
- package/dist/angular.mjs +1310 -0
- package/dist/angular.mjs.map +1 -0
- package/dist/astro.d.mts +8 -2
- package/dist/astro.d.ts +8 -2
- package/dist/astro.js +400 -100
- package/dist/astro.js.map +1 -1
- package/dist/astro.mjs +400 -100
- package/dist/astro.mjs.map +1 -1
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1880 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +1878 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +183 -4
- package/dist/index.d.ts +183 -4
- package/dist/index.js +974 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +952 -20
- package/dist/index.mjs.map +1 -1
- package/dist/next.d.mts +2 -17
- package/dist/next.d.ts +2 -17
- package/dist/next.js +262 -73
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +262 -73
- package/dist/next.mjs.map +1 -1
- package/dist/nuxt.d.mts +13 -0
- package/dist/nuxt.d.ts +13 -0
- package/dist/nuxt.js +1344 -0
- package/dist/nuxt.js.map +1 -0
- package/dist/nuxt.mjs +1337 -0
- package/dist/nuxt.mjs.map +1 -0
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +330 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +330 -4
- package/dist/react.mjs.map +1 -1
- package/dist/{types-BTY-v-7i.d.mts → types-Cn_Qbkmg.d.mts} +34 -0
- package/dist/{types-BTY-v-7i.d.ts → types-Cn_Qbkmg.d.ts} +34 -0
- package/dist/vite.d.mts +5 -0
- package/dist/vite.d.ts +5 -0
- package/dist/vite.js +1370 -0
- package/dist/vite.js.map +1 -0
- package/dist/vite.mjs +1366 -0
- package/dist/vite.mjs.map +1 -0
- package/dist/vue.d.mts +19 -0
- package/dist/vue.d.ts +19 -0
- package/dist/vue.js +1404 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +1398 -0
- package/dist/vue.mjs.map +1 -0
- package/dist/webpack.d.mts +1 -1
- package/dist/webpack.d.ts +1 -1
- package/dist/webpack.js +178 -18
- package/dist/webpack.js.map +1 -1
- package/dist/webpack.mjs +178 -18
- package/dist/webpack.mjs.map +1 -1
- package/dist/widget.d.mts +11 -1
- package/dist/widget.d.ts +11 -1
- package/dist/widget.js +330 -4
- package/dist/widget.js.map +1 -1
- package/dist/widget.mjs +330 -4
- package/dist/widget.mjs.map +1 -1
- package/package.json +48 -2
package/dist/astro.mjs
CHANGED
|
@@ -162,7 +162,7 @@ function detectFramework(projectRoot = process.cwd()) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
function resolveConfig(config = {}) {
|
|
165
|
-
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;
|
|
165
|
+
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;
|
|
166
166
|
const frameworkInfo = detectFramework();
|
|
167
167
|
return {
|
|
168
168
|
title: config.title || "My Site",
|
|
@@ -178,26 +178,43 @@ function resolveConfig(config = {}) {
|
|
|
178
178
|
rawMarkdown: ((_d = config.generators) == null ? void 0 : _d.rawMarkdown) !== false,
|
|
179
179
|
manifest: ((_e = config.generators) == null ? void 0 : _e.manifest) !== false,
|
|
180
180
|
sitemap: ((_f = config.generators) == null ? void 0 : _f.sitemap) !== false,
|
|
181
|
-
aiIndex: ((_g = config.generators) == null ? void 0 : _g.aiIndex) !== false
|
|
181
|
+
aiIndex: ((_g = config.generators) == null ? void 0 : _g.aiIndex) !== false,
|
|
182
|
+
schema: ((_h = config.generators) == null ? void 0 : _h.schema) !== false
|
|
182
183
|
},
|
|
183
184
|
robots: {
|
|
184
|
-
allow: ((
|
|
185
|
-
disallow: ((
|
|
186
|
-
crawlDelay: ((
|
|
187
|
-
sitemap: ((
|
|
185
|
+
allow: ((_i = config.robots) == null ? void 0 : _i.allow) || ["/"],
|
|
186
|
+
disallow: ((_j = config.robots) == null ? void 0 : _j.disallow) || [],
|
|
187
|
+
crawlDelay: ((_k = config.robots) == null ? void 0 : _k.crawlDelay) || 0,
|
|
188
|
+
sitemap: ((_l = config.robots) == null ? void 0 : _l.sitemap) || ""
|
|
189
|
+
},
|
|
190
|
+
schema: {
|
|
191
|
+
enabled: ((_m = config.schema) == null ? void 0 : _m.enabled) !== false,
|
|
192
|
+
organization: {
|
|
193
|
+
name: ((_o = (_n = config.schema) == null ? void 0 : _n.organization) == null ? void 0 : _o.name) || config.title || "My Site",
|
|
194
|
+
url: ((_q = (_p = config.schema) == null ? void 0 : _p.organization) == null ? void 0 : _q.url) || config.url || "https://example.com",
|
|
195
|
+
logo: ((_s = (_r = config.schema) == null ? void 0 : _r.organization) == null ? void 0 : _s.logo) || "",
|
|
196
|
+
sameAs: ((_u = (_t = config.schema) == null ? void 0 : _t.organization) == null ? void 0 : _u.sameAs) || []
|
|
197
|
+
},
|
|
198
|
+
defaultType: ((_v = config.schema) == null ? void 0 : _v.defaultType) || "WebPage"
|
|
199
|
+
},
|
|
200
|
+
og: {
|
|
201
|
+
enabled: ((_w = config.og) == null ? void 0 : _w.enabled) !== false,
|
|
202
|
+
image: ((_x = config.og) == null ? void 0 : _x.image) || "",
|
|
203
|
+
twitterHandle: ((_y = config.og) == null ? void 0 : _y.twitterHandle) || "",
|
|
204
|
+
type: ((_z = config.og) == null ? void 0 : _z.type) || "website"
|
|
188
205
|
},
|
|
189
206
|
widget: {
|
|
190
|
-
enabled: ((
|
|
191
|
-
position: ((
|
|
207
|
+
enabled: ((_A = config.widget) == null ? void 0 : _A.enabled) !== false,
|
|
208
|
+
position: ((_B = config.widget) == null ? void 0 : _B.position) || "bottom-right",
|
|
192
209
|
theme: {
|
|
193
|
-
background: ((
|
|
194
|
-
text: ((
|
|
195
|
-
accent: ((
|
|
196
|
-
badge: ((
|
|
210
|
+
background: ((_D = (_C = config.widget) == null ? void 0 : _C.theme) == null ? void 0 : _D.background) || "rgba(18, 18, 24, 0.9)",
|
|
211
|
+
text: ((_F = (_E = config.widget) == null ? void 0 : _E.theme) == null ? void 0 : _F.text) || "#C0C0C5",
|
|
212
|
+
accent: ((_H = (_G = config.widget) == null ? void 0 : _G.theme) == null ? void 0 : _H.accent) || "#E8E8EA",
|
|
213
|
+
badge: ((_J = (_I = config.widget) == null ? void 0 : _I.theme) == null ? void 0 : _J.badge) || "#4ADE80"
|
|
197
214
|
},
|
|
198
|
-
humanLabel: ((
|
|
199
|
-
aiLabel: ((
|
|
200
|
-
showBadge: ((
|
|
215
|
+
humanLabel: ((_K = config.widget) == null ? void 0 : _K.humanLabel) || "Human",
|
|
216
|
+
aiLabel: ((_L = config.widget) == null ? void 0 : _L.aiLabel) || "AI",
|
|
217
|
+
showBadge: ((_M = config.widget) == null ? void 0 : _M.showBadge) !== false
|
|
201
218
|
}
|
|
202
219
|
};
|
|
203
220
|
}
|
|
@@ -496,8 +513,8 @@ function generatePageMarkdownFiles(config) {
|
|
|
496
513
|
const generated = [];
|
|
497
514
|
const pages = config.pages || [];
|
|
498
515
|
for (const page of pages) {
|
|
516
|
+
if (!page.content) continue;
|
|
499
517
|
const pageTitle = page.title || (page.pathname === "/" ? config.title : void 0);
|
|
500
|
-
if (!page.content && !pageTitle) continue;
|
|
501
518
|
let filename;
|
|
502
519
|
if (page.pathname === "/") {
|
|
503
520
|
filename = "index.md";
|
|
@@ -767,6 +784,147 @@ function generateAIIndex(config) {
|
|
|
767
784
|
};
|
|
768
785
|
return JSON.stringify(index, null, 2);
|
|
769
786
|
}
|
|
787
|
+
|
|
788
|
+
// src/core/schema.ts
|
|
789
|
+
function generateSchema(config) {
|
|
790
|
+
const output = generateSchemaObjects(config);
|
|
791
|
+
return JSON.stringify(output, null, 2);
|
|
792
|
+
}
|
|
793
|
+
function generateSchemaObjects(config) {
|
|
794
|
+
const siteSchemas = generateSiteSchemas(config);
|
|
795
|
+
const pageSchemas = {};
|
|
796
|
+
for (const page of config.pages) {
|
|
797
|
+
const schemas = generatePageSchemas(page, config);
|
|
798
|
+
if (schemas.length > 0) {
|
|
799
|
+
pageSchemas[page.pathname] = schemas;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return { site: siteSchemas, pages: pageSchemas };
|
|
803
|
+
}
|
|
804
|
+
function generateSiteSchemas(config) {
|
|
805
|
+
const schemas = [];
|
|
806
|
+
schemas.push({
|
|
807
|
+
"@context": "https://schema.org",
|
|
808
|
+
"@type": "WebSite",
|
|
809
|
+
name: config.title,
|
|
810
|
+
description: config.description || void 0,
|
|
811
|
+
url: config.url
|
|
812
|
+
});
|
|
813
|
+
const org = config.schema.organization;
|
|
814
|
+
if (org.name || org.sameAs.length > 0) {
|
|
815
|
+
const orgSchema = {
|
|
816
|
+
"@context": "https://schema.org",
|
|
817
|
+
"@type": "Organization",
|
|
818
|
+
name: org.name,
|
|
819
|
+
url: org.url
|
|
820
|
+
};
|
|
821
|
+
if (org.logo) orgSchema.logo = org.logo;
|
|
822
|
+
if (org.sameAs.length > 0) orgSchema.sameAs = org.sameAs;
|
|
823
|
+
schemas.push(orgSchema);
|
|
824
|
+
}
|
|
825
|
+
return schemas;
|
|
826
|
+
}
|
|
827
|
+
function generatePageSchemas(page, config) {
|
|
828
|
+
const schemas = [];
|
|
829
|
+
const pageUrl = page.pathname === "/" ? config.url : `${config.url.replace(/\/$/, "")}${page.pathname}`;
|
|
830
|
+
const faqItems = detectFaqPatterns(page.content || "");
|
|
831
|
+
if (faqItems.length > 0) {
|
|
832
|
+
schemas.push({
|
|
833
|
+
"@context": "https://schema.org",
|
|
834
|
+
"@type": "FAQPage",
|
|
835
|
+
mainEntity: faqItems.map(({ question, answer }) => ({
|
|
836
|
+
"@type": "Question",
|
|
837
|
+
name: question,
|
|
838
|
+
acceptedAnswer: {
|
|
839
|
+
"@type": "Answer",
|
|
840
|
+
text: answer
|
|
841
|
+
}
|
|
842
|
+
}))
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
const pageType = config.schema.defaultType;
|
|
846
|
+
const pageSchema = {
|
|
847
|
+
"@context": "https://schema.org",
|
|
848
|
+
"@type": pageType,
|
|
849
|
+
name: page.title || config.title,
|
|
850
|
+
url: pageUrl
|
|
851
|
+
};
|
|
852
|
+
if (page.description) pageSchema.description = page.description;
|
|
853
|
+
if (pageType === "Article") {
|
|
854
|
+
pageSchema.headline = page.title || config.title;
|
|
855
|
+
pageSchema.author = {
|
|
856
|
+
"@type": "Organization",
|
|
857
|
+
name: config.schema.organization.name
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
schemas.push(pageSchema);
|
|
861
|
+
if (page.pathname !== "/") {
|
|
862
|
+
const breadcrumbs = generateBreadcrumbs(page.pathname, config);
|
|
863
|
+
if (breadcrumbs.length > 1) {
|
|
864
|
+
schemas.push({
|
|
865
|
+
"@context": "https://schema.org",
|
|
866
|
+
"@type": "BreadcrumbList",
|
|
867
|
+
itemListElement: breadcrumbs.map((crumb, i) => ({
|
|
868
|
+
"@type": "ListItem",
|
|
869
|
+
position: i + 1,
|
|
870
|
+
name: crumb.name,
|
|
871
|
+
item: crumb.url
|
|
872
|
+
}))
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return schemas;
|
|
877
|
+
}
|
|
878
|
+
function generateBreadcrumbs(pathname, config) {
|
|
879
|
+
const baseUrl = config.url.replace(/\/$/, "");
|
|
880
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
881
|
+
const crumbs = [
|
|
882
|
+
{ name: "Home", url: baseUrl + "/" }
|
|
883
|
+
];
|
|
884
|
+
let currentPath = "";
|
|
885
|
+
for (const part of parts) {
|
|
886
|
+
currentPath += "/" + part;
|
|
887
|
+
crumbs.push({
|
|
888
|
+
name: part.charAt(0).toUpperCase() + part.slice(1).replace(/-/g, " "),
|
|
889
|
+
url: baseUrl + currentPath
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
return crumbs;
|
|
893
|
+
}
|
|
894
|
+
function detectFaqPatterns(content) {
|
|
895
|
+
const items = [];
|
|
896
|
+
const lines = content.split("\n");
|
|
897
|
+
for (let i = 0; i < lines.length; i++) {
|
|
898
|
+
const line = lines[i].trim();
|
|
899
|
+
const headingMatch = line.match(/^#{1,6}\s+(.+\?)\s*$/);
|
|
900
|
+
if (headingMatch) {
|
|
901
|
+
const answerLines = [];
|
|
902
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
903
|
+
const nextLine = lines[j].trim();
|
|
904
|
+
if (!nextLine) {
|
|
905
|
+
if (answerLines.length > 0) break;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
if (/^#{1,6}\s/.test(nextLine)) break;
|
|
909
|
+
answerLines.push(nextLine);
|
|
910
|
+
}
|
|
911
|
+
if (answerLines.length > 0) {
|
|
912
|
+
items.push({
|
|
913
|
+
question: headingMatch[1],
|
|
914
|
+
answer: answerLines.join(" ").slice(0, 500)
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return items;
|
|
920
|
+
}
|
|
921
|
+
function generateJsonLdScript(schemas) {
|
|
922
|
+
if (schemas.length === 0) return "";
|
|
923
|
+
if (schemas.length === 1) {
|
|
924
|
+
return `<script type="application/ld+json">${JSON.stringify(schemas[0])}</script>`;
|
|
925
|
+
}
|
|
926
|
+
return schemas.map((s) => `<script type="application/ld+json">${JSON.stringify(s)}</script>`).join("\n");
|
|
927
|
+
}
|
|
770
928
|
async function generateAEOFiles(configOrRoot, maybeConfig) {
|
|
771
929
|
var _a;
|
|
772
930
|
let config;
|
|
@@ -795,7 +953,7 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
|
|
|
795
953
|
if (config.generators.llmsTxt) {
|
|
796
954
|
try {
|
|
797
955
|
const content = generateLlmsTxt(config);
|
|
798
|
-
writeFileSync(join(outDir, "llms.txt"),
|
|
956
|
+
writeFileSync(join(outDir, "llms.txt"), content, "utf-8");
|
|
799
957
|
files.push("llms.txt");
|
|
800
958
|
} catch (e) {
|
|
801
959
|
errors.push(`llms.txt: ${e.message}`);
|
|
@@ -804,7 +962,7 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
|
|
|
804
962
|
if (config.generators.llmsFullTxt) {
|
|
805
963
|
try {
|
|
806
964
|
const content = generateLlmsFullTxt(config);
|
|
807
|
-
writeFileSync(join(outDir, "llms-full.txt"),
|
|
965
|
+
writeFileSync(join(outDir, "llms-full.txt"), content, "utf-8");
|
|
808
966
|
files.push("llms-full.txt");
|
|
809
967
|
} catch (e) {
|
|
810
968
|
errors.push(`llms-full.txt: ${e.message}`);
|
|
@@ -855,12 +1013,131 @@ async function generateAEOFiles(configOrRoot, maybeConfig) {
|
|
|
855
1013
|
errors.push(`ai-index.json: ${e.message}`);
|
|
856
1014
|
}
|
|
857
1015
|
}
|
|
1016
|
+
if (config.generators.schema && config.schema.enabled) {
|
|
1017
|
+
try {
|
|
1018
|
+
const content = generateSchema(config);
|
|
1019
|
+
writeFileSync(join(outDir, "schema.json"), content, "utf-8");
|
|
1020
|
+
files.push("schema.json");
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
errors.push(`schema.json: ${e.message}`);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
858
1025
|
return { files, errors };
|
|
859
1026
|
}
|
|
860
|
-
|
|
1027
|
+
|
|
1028
|
+
// src/core/html-extract.ts
|
|
1029
|
+
function extractTextFromHtml(html) {
|
|
1030
|
+
let text = html;
|
|
1031
|
+
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
1032
|
+
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
1033
|
+
text = text.replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
1034
|
+
const mainMatch = text.match(/<main[^>]*>([\s\S]*)<\/main>/i);
|
|
1035
|
+
if (mainMatch) {
|
|
1036
|
+
text = mainMatch[1];
|
|
1037
|
+
} else {
|
|
1038
|
+
text = text.replace(/<nav[\s\S]*?<\/nav>/gi, "");
|
|
1039
|
+
text = text.replace(/<header[\s\S]*?<\/header>/gi, "");
|
|
1040
|
+
text = text.replace(/<footer[\s\S]*?<\/footer>/gi, "");
|
|
1041
|
+
}
|
|
1042
|
+
text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, url, inner) => {
|
|
1043
|
+
if (/<(?:h[1-6]|div|p|section)[^>]*>/i.test(inner)) {
|
|
1044
|
+
const cleanInner = inner.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
1045
|
+
return `
|
|
1046
|
+
[${cleanInner.slice(0, 120).trim()}](${url})
|
|
1047
|
+
`;
|
|
1048
|
+
}
|
|
1049
|
+
return `[${inner}](${url})`;
|
|
1050
|
+
});
|
|
1051
|
+
text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n\n## $1\n\n");
|
|
1052
|
+
text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n\n## $1\n\n");
|
|
1053
|
+
text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n\n### $1\n\n");
|
|
1054
|
+
text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n\n#### $1\n\n");
|
|
1055
|
+
text = text.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, "\n\n##### $1\n\n");
|
|
1056
|
+
text = text.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, "\n\n###### $1\n\n");
|
|
1057
|
+
text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
|
|
1058
|
+
text = text.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
|
|
1059
|
+
text = text.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
|
|
1060
|
+
text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
|
|
1061
|
+
text = text.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, "\n\n> $1\n\n");
|
|
1062
|
+
text = text.replace(/<hr[^>]*\/?>/gi, "\n\n---\n\n");
|
|
1063
|
+
text = text.replace(/<br[^>]*\/?>/gi, "\n");
|
|
1064
|
+
text = text.replace(/<\/p>/gi, "\n\n");
|
|
1065
|
+
text = text.replace(/<p[^>]*>/gi, "");
|
|
1066
|
+
text = text.replace(/<\/?(?:div|section|article|header|main|aside|figure|figcaption|table|thead|tbody|tr|td|th|ul|ol|dl|dt|dd)[^>]*>/gi, "\n");
|
|
1067
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
1068
|
+
text = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/©/g, "(c)");
|
|
1069
|
+
text = text.replace(/[\u{1F1E0}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}]/gu, "");
|
|
1070
|
+
text = text.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).join("\n");
|
|
1071
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
1072
|
+
text = text.replace(/\[[\s\n]+/g, "[").replace(/[\s\n]+\]/g, "]");
|
|
1073
|
+
text = text.replace(/(#{2,6})\s*\n+\s*/g, "$1 ");
|
|
1074
|
+
text = text.replace(/^#{2,6}\s*$/gm, "");
|
|
1075
|
+
text = text.replace(/\n{3,}/g, "\n\n");
|
|
1076
|
+
return text.trim().slice(0, 8e3);
|
|
1077
|
+
}
|
|
1078
|
+
function extractTitle2(html) {
|
|
1079
|
+
var _a, _b;
|
|
1080
|
+
const match = html.match(/<title>([^<]*)<\/title>/i);
|
|
1081
|
+
if (!match) return void 0;
|
|
1082
|
+
return ((_b = (_a = match[1]) == null ? void 0 : _a.split("|")[0]) == null ? void 0 : _b.trim()) || match[1];
|
|
1083
|
+
}
|
|
1084
|
+
function extractDescription(html) {
|
|
1085
|
+
const match = html.match(/<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i);
|
|
1086
|
+
return match == null ? void 0 : match[1];
|
|
1087
|
+
}
|
|
1088
|
+
function htmlToMarkdown(html, pagePath, config) {
|
|
1089
|
+
const rawTitle = extractTitle2(html);
|
|
1090
|
+
const description = extractDescription(html);
|
|
1091
|
+
const textContent = extractTextFromHtml(html);
|
|
1092
|
+
const pageUrl = pagePath === "/" ? config.url : `${config.url.replace(/\/$/, "")}${pagePath}`;
|
|
1093
|
+
const lines = [];
|
|
1094
|
+
lines.push("---");
|
|
1095
|
+
if (rawTitle) lines.push(`title: "${rawTitle}"`);
|
|
1096
|
+
if (description) lines.push(`description: "${description}"`);
|
|
1097
|
+
lines.push(`url: ${pageUrl}`);
|
|
1098
|
+
lines.push(`source: ${pageUrl}`);
|
|
1099
|
+
lines.push(`generated_by: aeo.js`);
|
|
1100
|
+
lines.push("---", "");
|
|
1101
|
+
if (rawTitle) lines.push(`# ${rawTitle}`, "");
|
|
1102
|
+
if (description) lines.push(`${description}`, "");
|
|
1103
|
+
if (textContent) lines.push(textContent);
|
|
1104
|
+
return lines.join("\n");
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// src/core/opengraph.ts
|
|
1108
|
+
function generateOGTags(page, config) {
|
|
1109
|
+
const tags = [];
|
|
1110
|
+
const pageUrl = page.pathname === "/" ? config.url : `${config.url.replace(/\/$/, "")}${page.pathname}`;
|
|
1111
|
+
const title = page.title || config.title;
|
|
1112
|
+
const description = page.description || config.description;
|
|
1113
|
+
tags.push({ property: "og:type", content: config.og.type });
|
|
1114
|
+
tags.push({ property: "og:title", content: title });
|
|
1115
|
+
if (description) tags.push({ property: "og:description", content: description });
|
|
1116
|
+
tags.push({ property: "og:url", content: pageUrl });
|
|
1117
|
+
tags.push({ property: "og:site_name", content: config.title });
|
|
1118
|
+
if (config.og.image) tags.push({ property: "og:image", content: config.og.image });
|
|
1119
|
+
tags.push({ name: "twitter:card", content: config.og.image ? "summary_large_image" : "summary" });
|
|
1120
|
+
tags.push({ name: "twitter:title", content: title });
|
|
1121
|
+
if (description) tags.push({ name: "twitter:description", content: description });
|
|
1122
|
+
if (config.og.twitterHandle) tags.push({ name: "twitter:site", content: config.og.twitterHandle });
|
|
1123
|
+
if (config.og.image) tags.push({ name: "twitter:image", content: config.og.image });
|
|
1124
|
+
return tags;
|
|
1125
|
+
}
|
|
1126
|
+
function generateOGTagsHtml(page, config) {
|
|
1127
|
+
const tags = generateOGTags(page, config);
|
|
1128
|
+
return tags.map((tag) => {
|
|
1129
|
+
if (tag.property) return `<meta property="${tag.property}" content="${escapeAttr(tag.content)}" />`;
|
|
1130
|
+
return `<meta name="${tag.name}" content="${escapeAttr(tag.content)}" />`;
|
|
1131
|
+
}).join("\n ");
|
|
1132
|
+
}
|
|
1133
|
+
function escapeAttr(str) {
|
|
1134
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// src/plugins/astro.ts
|
|
1138
|
+
function scanBuiltPages(dir, _baseUrl) {
|
|
861
1139
|
const pages = [];
|
|
862
1140
|
function walk(currentDir) {
|
|
863
|
-
var _a;
|
|
864
1141
|
try {
|
|
865
1142
|
const entries = readdirSync(currentDir);
|
|
866
1143
|
for (const entry of entries) {
|
|
@@ -871,8 +1148,8 @@ function scanBuiltPages(dir, baseUrl) {
|
|
|
871
1148
|
} else if (entry === "index.html" || entry.endsWith(".html") && entry !== "404.html" && entry !== "500.html") {
|
|
872
1149
|
try {
|
|
873
1150
|
const html = readFileSync(fullPath, "utf-8");
|
|
874
|
-
const
|
|
875
|
-
const
|
|
1151
|
+
const title = extractTitle2(html);
|
|
1152
|
+
const description = extractDescription(html);
|
|
876
1153
|
const textContent = extractTextFromHtml(html);
|
|
877
1154
|
let pathname;
|
|
878
1155
|
const relative8 = fullPath.slice(dir.length);
|
|
@@ -883,13 +1160,12 @@ function scanBuiltPages(dir, baseUrl) {
|
|
|
883
1160
|
pathname = "/" + relative8.replace(/\.html$/, "");
|
|
884
1161
|
}
|
|
885
1162
|
pathname = pathname.replace(/\/+/g, "/") || "/";
|
|
886
|
-
const rawTitle = titleMatch ? titleMatch[1] : void 0;
|
|
887
|
-
const title = ((_a = rawTitle == null ? void 0 : rawTitle.split("|")[0]) == null ? void 0 : _a.trim()) || rawTitle;
|
|
888
1163
|
pages.push({
|
|
889
1164
|
pathname,
|
|
890
1165
|
title,
|
|
891
|
-
description
|
|
892
|
-
content: textContent
|
|
1166
|
+
description,
|
|
1167
|
+
content: textContent,
|
|
1168
|
+
filePath: fullPath
|
|
893
1169
|
});
|
|
894
1170
|
} catch {
|
|
895
1171
|
}
|
|
@@ -933,75 +1209,54 @@ function scanDevPages(pagesDir) {
|
|
|
933
1209
|
}
|
|
934
1210
|
return pages;
|
|
935
1211
|
}
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
939
|
-
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
940
|
-
text = text.replace(/<svg[\s\S]*?<\/svg>/gi, "");
|
|
941
|
-
const mainMatch = text.match(/<main[^>]*>([\s\S]*)<\/main>/i);
|
|
942
|
-
if (mainMatch) {
|
|
943
|
-
text = mainMatch[1];
|
|
944
|
-
} else {
|
|
945
|
-
text = text.replace(/<nav[\s\S]*?<\/nav>/gi, "");
|
|
946
|
-
text = text.replace(/<header[\s\S]*?<\/header>/gi, "");
|
|
947
|
-
text = text.replace(/<footer[\s\S]*?<\/footer>/gi, "");
|
|
948
|
-
}
|
|
949
|
-
text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, url, inner) => {
|
|
950
|
-
if (/<(?:h[1-6]|div|p|section)[^>]*>/i.test(inner)) {
|
|
951
|
-
const cleanInner = inner.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
952
|
-
return `
|
|
953
|
-
[${cleanInner.slice(0, 120).trim()}](${url})
|
|
954
|
-
`;
|
|
955
|
-
}
|
|
956
|
-
return `[${inner}](${url})`;
|
|
957
|
-
});
|
|
958
|
-
text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, "\n\n## $1\n\n");
|
|
959
|
-
text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, "\n\n## $1\n\n");
|
|
960
|
-
text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, "\n\n### $1\n\n");
|
|
961
|
-
text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, "\n\n#### $1\n\n");
|
|
962
|
-
text = text.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, "\n\n##### $1\n\n");
|
|
963
|
-
text = text.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, "\n\n###### $1\n\n");
|
|
964
|
-
text = text.replace(/<a[^>]+href=["']([^"']*)["'][^>]*>([\s\S]*?)<\/a>/gi, "[$2]($1)");
|
|
965
|
-
text = text.replace(/<(?:strong|b)[^>]*>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
|
|
966
|
-
text = text.replace(/<(?:em|i)[^>]*>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
|
|
967
|
-
text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, "\n- $1");
|
|
968
|
-
text = text.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, "\n\n> $1\n\n");
|
|
969
|
-
text = text.replace(/<hr[^>]*\/?>/gi, "\n\n---\n\n");
|
|
970
|
-
text = text.replace(/<br[^>]*\/?>/gi, "\n");
|
|
971
|
-
text = text.replace(/<\/p>/gi, "\n\n");
|
|
972
|
-
text = text.replace(/<p[^>]*>/gi, "");
|
|
973
|
-
text = text.replace(/<\/?(?:div|section|article|header|main|aside|figure|figcaption|table|thead|tbody|tr|td|th|ul|ol|dl|dt|dd)[^>]*>/gi, "\n");
|
|
974
|
-
text = text.replace(/<[^>]+>/g, "");
|
|
975
|
-
text = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/©/g, "(c)");
|
|
976
|
-
text = text.replace(/[\u{1F1E0}-\u{1FAFF}\u{2600}-\u{27BF}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}]/gu, "");
|
|
977
|
-
text = text.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).join("\n");
|
|
978
|
-
text = text.replace(/\n{3,}/g, "\n\n");
|
|
979
|
-
text = text.replace(/\[[\s\n]+/g, "[").replace(/[\s\n]+\]/g, "]");
|
|
980
|
-
text = text.replace(/(#{2,6})\s*\n+\s*/g, "$1 ");
|
|
981
|
-
text = text.replace(/^#{2,6}\s*$/gm, "");
|
|
982
|
-
text = text.replace(/\n{3,}/g, "\n\n");
|
|
983
|
-
return text.trim().slice(0, 8e3);
|
|
1212
|
+
function escapeAttr2(str) {
|
|
1213
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
984
1214
|
}
|
|
985
|
-
function
|
|
986
|
-
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1215
|
+
function injectHeadTags(pages, config) {
|
|
1216
|
+
let injectedCount = 0;
|
|
1217
|
+
for (const page of pages) {
|
|
1218
|
+
let html;
|
|
1219
|
+
try {
|
|
1220
|
+
html = readFileSync(page.filePath, "utf-8");
|
|
1221
|
+
} catch {
|
|
1222
|
+
continue;
|
|
1223
|
+
}
|
|
1224
|
+
const tags = [];
|
|
1225
|
+
if (!/name=["']description["']/i.test(html)) {
|
|
1226
|
+
const desc = page.description || config.description;
|
|
1227
|
+
if (desc) {
|
|
1228
|
+
tags.push(`<meta name="description" content="${escapeAttr2(desc)}" />`);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
if (!/rel=["']canonical["']/i.test(html)) {
|
|
1232
|
+
const pageUrl = page.pathname === "/" ? config.url : `${config.url.replace(/\/$/, "")}${page.pathname}`;
|
|
1233
|
+
tags.push(`<link rel="canonical" href="${escapeAttr2(pageUrl)}" />`);
|
|
1234
|
+
}
|
|
1235
|
+
if (config.og.enabled && !/property=["']og:/i.test(html)) {
|
|
1236
|
+
const ogHtml = generateOGTagsHtml(page, config);
|
|
1237
|
+
if (ogHtml) tags.push(ogHtml);
|
|
1238
|
+
}
|
|
1239
|
+
if (config.schema.enabled && !/application\/ld\+json/i.test(html)) {
|
|
1240
|
+
const siteSchemas = generateSiteSchemas(config);
|
|
1241
|
+
const pageSchemas = generatePageSchemas(page, config);
|
|
1242
|
+
const jsonLdHtml = generateJsonLdScript([...siteSchemas, ...pageSchemas]);
|
|
1243
|
+
if (jsonLdHtml) tags.push(jsonLdHtml);
|
|
1244
|
+
}
|
|
1245
|
+
if (!/rel=["']alternate["'][^>]*llms\.txt/i.test(html)) {
|
|
1246
|
+
tags.push(`<link rel="alternate" type="text/plain" href="/llms.txt" title="LLM Summary" />`);
|
|
1247
|
+
tags.push(`<link rel="alternate" type="text/plain" href="/llms-full.txt" title="Full Content for LLMs" />`);
|
|
1248
|
+
tags.push(`<link rel="alternate" type="application/json" href="/docs.json" title="Documentation Manifest" />`);
|
|
1249
|
+
tags.push(`<link rel="alternate" type="application/json" href="/ai-index.json" title="AI-Optimized Index" />`);
|
|
1250
|
+
}
|
|
1251
|
+
if (tags.length === 0) continue;
|
|
1252
|
+
const injection = "\n " + tags.join("\n ") + "\n ";
|
|
1253
|
+
const newHtml = html.replace("</head>", injection + "</head>");
|
|
1254
|
+
if (newHtml !== html) {
|
|
1255
|
+
writeFileSync(page.filePath, newHtml, "utf-8");
|
|
1256
|
+
injectedCount++;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return injectedCount;
|
|
1005
1260
|
}
|
|
1006
1261
|
function aeoAstroIntegration(options = {}) {
|
|
1007
1262
|
let resolvedConfig = resolveConfig(options);
|
|
@@ -1023,14 +1278,33 @@ function aeoAstroIntegration(options = {}) {
|
|
|
1023
1278
|
}
|
|
1024
1279
|
}
|
|
1025
1280
|
if (resolvedConfig.widget.enabled && injectScript) {
|
|
1026
|
-
const
|
|
1281
|
+
const widgetConfig = JSON.stringify({
|
|
1282
|
+
title: resolvedConfig.title,
|
|
1283
|
+
description: resolvedConfig.description,
|
|
1284
|
+
url: resolvedConfig.url,
|
|
1285
|
+
widget: resolvedConfig.widget
|
|
1286
|
+
});
|
|
1027
1287
|
injectScript(
|
|
1028
1288
|
"page",
|
|
1029
1289
|
`import { AeoWidget } from 'aeo.js/widget';
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1290
|
+
let __aeoWidget;
|
|
1291
|
+
function __initAeoWidget() {
|
|
1292
|
+
if (__aeoWidget) __aeoWidget.destroy();
|
|
1293
|
+
try {
|
|
1294
|
+
__aeoWidget = new AeoWidget({ config: ${widgetConfig} });
|
|
1295
|
+
} catch (e) {
|
|
1296
|
+
console.warn('[aeo.js] Widget initialization failed:', e);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
// astro:page-load fires on initial load AND after every View Transition navigation
|
|
1300
|
+
document.addEventListener('astro:page-load', __initAeoWidget);
|
|
1301
|
+
// Fallback for Astro sites without View Transitions
|
|
1302
|
+
if (!document.querySelector('meta[name="astro-view-transitions-enabled"]')) {
|
|
1303
|
+
if (document.readyState === 'loading') {
|
|
1304
|
+
document.addEventListener('DOMContentLoaded', __initAeoWidget);
|
|
1305
|
+
} else {
|
|
1306
|
+
__initAeoWidget();
|
|
1307
|
+
}
|
|
1034
1308
|
}`
|
|
1035
1309
|
);
|
|
1036
1310
|
}
|
|
@@ -1064,6 +1338,14 @@ if (document.readyState === 'loading') {
|
|
|
1064
1338
|
} catch (error) {
|
|
1065
1339
|
buildLogger.error(`Failed to generate AEO files: ${error}`);
|
|
1066
1340
|
}
|
|
1341
|
+
try {
|
|
1342
|
+
const injected = injectHeadTags(discoveredPages, resolvedConfig);
|
|
1343
|
+
if (injected > 0) {
|
|
1344
|
+
buildLogger.info(`Injected head tags into ${injected} pages`);
|
|
1345
|
+
}
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
buildLogger.error(`Failed to inject head tags: ${error}`);
|
|
1348
|
+
}
|
|
1067
1349
|
},
|
|
1068
1350
|
"astro:server:setup": async ({ server, logger }) => {
|
|
1069
1351
|
const devLogger = logger.fork("aeo.js");
|
|
@@ -1150,8 +1432,24 @@ if (document.readyState === 'loading') {
|
|
|
1150
1432
|
}
|
|
1151
1433
|
};
|
|
1152
1434
|
}
|
|
1153
|
-
var AeoMetaTags = ({ config }) => {
|
|
1435
|
+
var AeoMetaTags = ({ config, page }) => {
|
|
1154
1436
|
const resolvedConfig = resolveConfig(config);
|
|
1437
|
+
const currentPage = page || { pathname: "/" };
|
|
1438
|
+
const pageEntry = {
|
|
1439
|
+
pathname: currentPage.pathname || "/",
|
|
1440
|
+
title: currentPage.title,
|
|
1441
|
+
description: currentPage.description,
|
|
1442
|
+
content: currentPage.content
|
|
1443
|
+
};
|
|
1444
|
+
let jsonLd = "";
|
|
1445
|
+
if (resolvedConfig.schema.enabled) {
|
|
1446
|
+
const schemas = generatePageSchemas(pageEntry, resolvedConfig);
|
|
1447
|
+
jsonLd = generateJsonLdScript(schemas);
|
|
1448
|
+
}
|
|
1449
|
+
let ogTags = "";
|
|
1450
|
+
if (resolvedConfig.og.enabled) {
|
|
1451
|
+
ogTags = generateOGTagsHtml(pageEntry, resolvedConfig);
|
|
1452
|
+
}
|
|
1155
1453
|
return `
|
|
1156
1454
|
<link rel="alternate" type="text/plain" href="/llms.txt" title="LLM Summary" />
|
|
1157
1455
|
<link rel="alternate" type="text/plain" href="/llms-full.txt" title="Full Content for LLMs" />
|
|
@@ -1160,6 +1458,8 @@ var AeoMetaTags = ({ config }) => {
|
|
|
1160
1458
|
<meta name="aeo:title" content="${resolvedConfig.title}" />
|
|
1161
1459
|
<meta name="aeo:description" content="${resolvedConfig.description}" />
|
|
1162
1460
|
<meta name="aeo:url" content="${resolvedConfig.url}" />
|
|
1461
|
+
${ogTags}
|
|
1462
|
+
${jsonLd}
|
|
1163
1463
|
`;
|
|
1164
1464
|
};
|
|
1165
1465
|
function defineAeoConfig(config) {
|