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