@seo-partner/adapters 0.0.0-stage → 1.0.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.
- package/LICENSE +21 -0
- package/dist/ast-merger.d.ts +24 -0
- package/dist/ast-merger.d.ts.map +1 -0
- package/dist/ast-merger.js +85 -0
- package/dist/ast-merger.js.map +1 -0
- package/dist/astro-adapter.d.ts +19 -0
- package/dist/astro-adapter.d.ts.map +1 -0
- package/dist/astro-adapter.js +114 -0
- package/dist/astro-adapter.js.map +1 -0
- package/dist/blog-scaffold-generator.d.ts +8 -0
- package/dist/blog-scaffold-generator.d.ts.map +1 -0
- package/dist/blog-scaffold-generator.js +138 -0
- package/dist/blog-scaffold-generator.js.map +1 -0
- package/dist/contract.d.ts +21 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +2 -0
- package/dist/contract.js.map +1 -0
- package/dist/detector.d.ts +15 -0
- package/dist/detector.d.ts.map +1 -0
- package/dist/detector.js +40 -0
- package/dist/detector.js.map +1 -0
- package/dist/django-adapter.d.ts +13 -0
- package/dist/django-adapter.d.ts.map +1 -0
- package/dist/django-adapter.js +133 -0
- package/dist/django-adapter.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/laravel-adapter.d.ts +7 -0
- package/dist/laravel-adapter.d.ts.map +1 -0
- package/dist/laravel-adapter.js +35 -0
- package/dist/laravel-adapter.js.map +1 -0
- package/dist/layout-title-resolver.d.ts +8 -0
- package/dist/layout-title-resolver.d.ts.map +1 -0
- package/dist/layout-title-resolver.js +28 -0
- package/dist/layout-title-resolver.js.map +1 -0
- package/dist/nextjs-adapter.d.ts +15 -0
- package/dist/nextjs-adapter.d.ts.map +1 -0
- package/dist/nextjs-adapter.js +83 -0
- package/dist/nextjs-adapter.js.map +1 -0
- package/dist/nuxt-adapter.d.ts +19 -0
- package/dist/nuxt-adapter.d.ts.map +1 -0
- package/dist/nuxt-adapter.js +108 -0
- package/dist/nuxt-adapter.js.map +1 -0
- package/dist/parsers/astro-parser.d.ts +8 -0
- package/dist/parsers/astro-parser.d.ts.map +1 -0
- package/dist/parsers/astro-parser.js +218 -0
- package/dist/parsers/astro-parser.js.map +1 -0
- package/dist/parsers/dtl-parser.d.ts +7 -0
- package/dist/parsers/dtl-parser.d.ts.map +1 -0
- package/dist/parsers/dtl-parser.js +143 -0
- package/dist/parsers/dtl-parser.js.map +1 -0
- package/dist/parsers/html-parser.d.ts +3 -0
- package/dist/parsers/html-parser.d.ts.map +1 -0
- package/dist/parsers/html-parser.js +195 -0
- package/dist/parsers/html-parser.js.map +1 -0
- package/dist/parsers/layout-resolver.d.ts +44 -0
- package/dist/parsers/layout-resolver.d.ts.map +1 -0
- package/dist/parsers/layout-resolver.js +85 -0
- package/dist/parsers/layout-resolver.js.map +1 -0
- package/dist/parsers/php-route-parser.d.ts +11 -0
- package/dist/parsers/php-route-parser.d.ts.map +1 -0
- package/dist/parsers/php-route-parser.js +194 -0
- package/dist/parsers/php-route-parser.js.map +1 -0
- package/dist/parsers/php-template-parser.d.ts +3 -0
- package/dist/parsers/php-template-parser.d.ts.map +1 -0
- package/dist/parsers/php-template-parser.js +230 -0
- package/dist/parsers/php-template-parser.js.map +1 -0
- package/dist/parsers/python-url-parser.d.ts +19 -0
- package/dist/parsers/python-url-parser.d.ts.map +1 -0
- package/dist/parsers/python-url-parser.js +69 -0
- package/dist/parsers/python-url-parser.js.map +1 -0
- package/dist/parsers/remix-parser.d.ts +3 -0
- package/dist/parsers/remix-parser.d.ts.map +1 -0
- package/dist/parsers/remix-parser.js +139 -0
- package/dist/parsers/remix-parser.js.map +1 -0
- package/dist/parsers/robots-sitemap-parser.d.ts +15 -0
- package/dist/parsers/robots-sitemap-parser.d.ts.map +1 -0
- package/dist/parsers/robots-sitemap-parser.js +100 -0
- package/dist/parsers/robots-sitemap-parser.js.map +1 -0
- package/dist/parsers/route-normalizer.d.ts +11 -0
- package/dist/parsers/route-normalizer.d.ts.map +1 -0
- package/dist/parsers/route-normalizer.js +59 -0
- package/dist/parsers/route-normalizer.js.map +1 -0
- package/dist/parsers/svelte-parser.d.ts +3 -0
- package/dist/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/parsers/svelte-parser.js +148 -0
- package/dist/parsers/svelte-parser.js.map +1 -0
- package/dist/parsers/tsx-ast-parser.d.ts +4 -0
- package/dist/parsers/tsx-ast-parser.d.ts.map +1 -0
- package/dist/parsers/tsx-ast-parser.js +380 -0
- package/dist/parsers/tsx-ast-parser.js.map +1 -0
- package/dist/parsers/vue-sfc-parser.d.ts +3 -0
- package/dist/parsers/vue-sfc-parser.d.ts.map +1 -0
- package/dist/parsers/vue-sfc-parser.js +183 -0
- package/dist/parsers/vue-sfc-parser.js.map +1 -0
- package/dist/php-adapter.d.ts +12 -0
- package/dist/php-adapter.d.ts.map +1 -0
- package/dist/php-adapter.js +42 -0
- package/dist/php-adapter.js.map +1 -0
- package/dist/php-native-adapter.d.ts +7 -0
- package/dist/php-native-adapter.d.ts.map +1 -0
- package/dist/php-native-adapter.js +31 -0
- package/dist/php-native-adapter.js.map +1 -0
- package/dist/remix-adapter.d.ts +13 -0
- package/dist/remix-adapter.d.ts.map +1 -0
- package/dist/remix-adapter.js +91 -0
- package/dist/remix-adapter.js.map +1 -0
- package/dist/sitemap-builder.d.ts +10 -0
- package/dist/sitemap-builder.d.ts.map +1 -0
- package/dist/sitemap-builder.js +37 -0
- package/dist/sitemap-builder.js.map +1 -0
- package/dist/skeleton-detector.d.ts +14 -0
- package/dist/skeleton-detector.d.ts.map +1 -0
- package/dist/skeleton-detector.js +33 -0
- package/dist/skeleton-detector.js.map +1 -0
- package/dist/static-adapter.d.ts +12 -0
- package/dist/static-adapter.d.ts.map +1 -0
- package/dist/static-adapter.js +44 -0
- package/dist/static-adapter.js.map +1 -0
- package/dist/sveltekit-adapter.d.ts +19 -0
- package/dist/sveltekit-adapter.d.ts.map +1 -0
- package/dist/sveltekit-adapter.js +115 -0
- package/dist/sveltekit-adapter.js.map +1 -0
- package/dist/symfony-adapter.d.ts +7 -0
- package/dist/symfony-adapter.d.ts.map +1 -0
- package/dist/symfony-adapter.js +23 -0
- package/dist/symfony-adapter.js.map +1 -0
- package/package.json +23 -3
- package/src/ast-merger.ts +123 -0
- package/src/astro-adapter.ts +155 -0
- package/src/blog-scaffold-generator.ts +153 -0
- package/src/contract.ts +40 -0
- package/src/detector.ts +46 -0
- package/src/django-adapter.ts +173 -0
- package/src/index.ts +31 -0
- package/src/laravel-adapter.ts +45 -0
- package/src/layout-title-resolver.ts +37 -0
- package/src/nextjs-adapter.ts +116 -0
- package/src/nuxt-adapter.ts +148 -0
- package/src/parsers/astro-parser.ts +249 -0
- package/src/parsers/dtl-parser.ts +166 -0
- package/src/parsers/html-parser.ts +214 -0
- package/src/parsers/layout-resolver.ts +112 -0
- package/src/parsers/php-route-parser.ts +254 -0
- package/src/parsers/php-template-parser.ts +303 -0
- package/src/parsers/python-url-parser.ts +92 -0
- package/src/parsers/remix-parser.ts +161 -0
- package/src/parsers/robots-sitemap-parser.ts +128 -0
- package/src/parsers/route-normalizer.ts +70 -0
- package/src/parsers/svelte-parser.ts +167 -0
- package/src/parsers/tsx-ast-parser.ts +404 -0
- package/src/parsers/vue-sfc-parser.ts +207 -0
- package/src/php-adapter.ts +68 -0
- package/src/php-native-adapter.ts +46 -0
- package/src/remix-adapter.ts +123 -0
- package/src/sitemap-builder.ts +57 -0
- package/src/skeleton-detector.ts +56 -0
- package/src/static-adapter.ts +62 -0
- package/src/sveltekit-adapter.ts +160 -0
- package/src/symfony-adapter.ts +33 -0
- package/tsconfig.json +9 -0
- package/README.md +0 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { normalizeRoutePattern } from './route-normalizer.js';
|
|
2
|
+
function lineNumberAtOffset(content, offset) {
|
|
3
|
+
let line = 1;
|
|
4
|
+
for (let i = 0; i < offset && i < content.length; i += 1) {
|
|
5
|
+
if (content.charCodeAt(i) === 10)
|
|
6
|
+
line += 1;
|
|
7
|
+
}
|
|
8
|
+
return line;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Statically parses a Django urls.py file to extract route declarations without executing Python.
|
|
12
|
+
*/
|
|
13
|
+
export function parseDjangoUrls(urlsPyContent) {
|
|
14
|
+
const routes = [];
|
|
15
|
+
// Match path('pattern', view, ...) or re_path(r'pattern', view, ...)
|
|
16
|
+
const pathRegex = /(?:path|re_path)\s*\(\s*(?:r?['"]([^'"]*)['"])\s*,\s*([a-zA-Z0-9_.]+(?:\.as_view\(\))?)/g;
|
|
17
|
+
let match;
|
|
18
|
+
while ((match = pathRegex.exec(urlsPyContent)) !== null) {
|
|
19
|
+
const rawPattern = match[1] ?? '';
|
|
20
|
+
const rawView = match[2] ?? '';
|
|
21
|
+
const line = lineNumberAtOffset(urlsPyContent, match.index);
|
|
22
|
+
// Clean view identifier (e.g. views.home_view -> home_view, views.ShopView.as_view() -> ShopView)
|
|
23
|
+
let viewName = rawView.replace(/\.as_view\(\)$/, '');
|
|
24
|
+
if (viewName.includes('.')) {
|
|
25
|
+
viewName = viewName.split('.').pop() ?? viewName;
|
|
26
|
+
}
|
|
27
|
+
const pattern = normalizeRoutePattern(rawPattern, 'django');
|
|
28
|
+
routes.push({
|
|
29
|
+
pattern,
|
|
30
|
+
rawPattern,
|
|
31
|
+
viewName,
|
|
32
|
+
lineNumber: line,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return routes;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Statically parses a Django views.py file to extract view -> template_name mappings.
|
|
39
|
+
*/
|
|
40
|
+
export function parseDjangoViews(viewsPyContent) {
|
|
41
|
+
const mappings = [];
|
|
42
|
+
// 1. Function-based views: def view_name(request, ...): ... render(request, 'template.html', ...)
|
|
43
|
+
const funcRegex = /def\s+([a-zA-Z0-9_]+)\s*\([^)]*\)\s*:([\s\S]*?)(?=\ndef|\nclass|$)/g;
|
|
44
|
+
let funcMatch;
|
|
45
|
+
while ((funcMatch = funcRegex.exec(viewsPyContent)) !== null) {
|
|
46
|
+
const viewName = funcMatch[1] ?? '';
|
|
47
|
+
const body = funcMatch[2] ?? '';
|
|
48
|
+
const renderMatch = /render\s*\(\s*[^,]+,\s*['"]([^'"]+\.html)['"]/i.exec(body);
|
|
49
|
+
if (renderMatch && renderMatch[1]) {
|
|
50
|
+
mappings.push({
|
|
51
|
+
viewName,
|
|
52
|
+
templateName: renderMatch[1],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// 2. Class-based views: class ViewName(...): ... template_name = 'template.html'
|
|
57
|
+
const classRegex = /class\s+([a-zA-Z0-9_]+)\b[\s\S]*?template_name\s*=\s*['"]([^'"]+\.html)['"]/g;
|
|
58
|
+
let classMatch;
|
|
59
|
+
while ((classMatch = classRegex.exec(viewsPyContent)) !== null) {
|
|
60
|
+
const viewName = classMatch[1] ?? '';
|
|
61
|
+
const tpl = classMatch[2] ?? '';
|
|
62
|
+
mappings.push({
|
|
63
|
+
viewName,
|
|
64
|
+
templateName: tpl,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return mappings;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=python-url-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-url-parser.js","sourceRoot":"","sources":["../../src/parsers/python-url-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remix-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/remix-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAKnB,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,kBAAkB,CAqIpB"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
function lineNumberAtOffset(content, offset) {
|
|
2
|
+
let line = 1;
|
|
3
|
+
for (let i = 0; i < offset && i < content.length; i += 1) {
|
|
4
|
+
if (content.charCodeAt(i) === 10)
|
|
5
|
+
line += 1;
|
|
6
|
+
}
|
|
7
|
+
return line;
|
|
8
|
+
}
|
|
9
|
+
function getAttr(attrsRaw, attrName) {
|
|
10
|
+
const re = new RegExp(`\\b${attrName}\\s*=\\s*["']([^"']*)["']`, 'i');
|
|
11
|
+
const m = re.exec(attrsRaw);
|
|
12
|
+
return m ? (m[1] ?? null) : null;
|
|
13
|
+
}
|
|
14
|
+
export function parseRemixRoute(route, fileContent) {
|
|
15
|
+
let title = null;
|
|
16
|
+
let description = null;
|
|
17
|
+
let canonical = null;
|
|
18
|
+
let robots = null;
|
|
19
|
+
let htmlLang = undefined;
|
|
20
|
+
let isDynamic = false;
|
|
21
|
+
const openGraph = {};
|
|
22
|
+
const hreflang = [];
|
|
23
|
+
const headings = [];
|
|
24
|
+
const images = [];
|
|
25
|
+
const links = [];
|
|
26
|
+
const jsonLdItems = [];
|
|
27
|
+
// 1. Check for <html lang="..."> in JSX (e.g. app/root.tsx)
|
|
28
|
+
const htmlTagMatch = /<html\b([^>]*?)>/i.exec(fileContent);
|
|
29
|
+
if (htmlTagMatch) {
|
|
30
|
+
const langVal = getAttr(htmlTagMatch[1] ?? '', 'lang');
|
|
31
|
+
if (langVal) {
|
|
32
|
+
htmlLang = {
|
|
33
|
+
value: langVal.trim(),
|
|
34
|
+
line: lineNumberAtOffset(fileContent, htmlTagMatch.index),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 2. Scan for export const meta = ...
|
|
39
|
+
// Pattern 1: array return in arrow function or array literal
|
|
40
|
+
// export const meta = () => [ ... ]; or export const meta = [ ... ];
|
|
41
|
+
const metaMatch = /export\s+const\s+meta\b[^=]*=\s*(?:(?:\([^)]*\)|[a-zA-Z0-9_]+)\s*=>\s*)?(?:\{[\s\S]*?return\s+)?\[([\s\S]*?)\]/i.exec(fileContent);
|
|
42
|
+
if (metaMatch && metaMatch[1]) {
|
|
43
|
+
const metaArrayContent = metaMatch[1];
|
|
44
|
+
const metaOffset = metaMatch.index;
|
|
45
|
+
const metaLine = lineNumberAtOffset(fileContent, metaOffset);
|
|
46
|
+
// Extract { title: "..." }
|
|
47
|
+
const titleMatch = /\{\s*title\s*:\s*["']([^"']+)["']\s*\}/i.exec(metaArrayContent);
|
|
48
|
+
if (titleMatch) {
|
|
49
|
+
title = { value: titleMatch[1] ?? '', line: metaLine };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Dynamic title expression: { title: data?.title || "..." } or { title: data ? ... }
|
|
53
|
+
const fallbackTitle = /\{\s*title\s*:\s*[^"'\n\r}]*?["']([^"']+)["']/i.exec(metaArrayContent);
|
|
54
|
+
if (fallbackTitle) {
|
|
55
|
+
title = { value: fallbackTitle[1] ?? '', line: metaLine };
|
|
56
|
+
}
|
|
57
|
+
else if (/\{\s*title\s*:/i.test(metaArrayContent)) {
|
|
58
|
+
title = { value: '[Valeur dynamique]', line: metaLine };
|
|
59
|
+
isDynamic = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Extract { name: "description", content: "..." }
|
|
63
|
+
const descMatch = /\{\s*name\s*:\s*["']description["']\s*,\s*content\s*:\s*["']([^"']+)["']/i.exec(metaArrayContent);
|
|
64
|
+
if (descMatch) {
|
|
65
|
+
description = { value: descMatch[1] ?? '', line: metaLine };
|
|
66
|
+
}
|
|
67
|
+
// Extract { name: "robots", content: "..." }
|
|
68
|
+
const robotsMatch = /\{\s*name\s*:\s*["']robots["']\s*,\s*content\s*:\s*["']([^"']+)["']/i.exec(metaArrayContent);
|
|
69
|
+
if (robotsMatch) {
|
|
70
|
+
robots = { value: robotsMatch[1] ?? '', line: metaLine };
|
|
71
|
+
}
|
|
72
|
+
// Extract canonical: { tagName: "link", rel: "canonical", href: "..." }
|
|
73
|
+
const canonicalMatch = /rel\s*:\s*["']canonical["']\s*,\s*href\s*:\s*["']([^"']+)["']/i.exec(metaArrayContent);
|
|
74
|
+
if (canonicalMatch) {
|
|
75
|
+
canonical = { value: canonicalMatch[1] ?? '', line: metaLine };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 3. Headings in JSX: <h1> to <h6>
|
|
79
|
+
const headingRegex = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
80
|
+
let hMatch;
|
|
81
|
+
while ((hMatch = headingRegex.exec(fileContent)) !== null) {
|
|
82
|
+
const level = Number(hMatch[1]);
|
|
83
|
+
const text = (hMatch[2] ?? '').replace(/<[^>]+>/g, '').replace(/\{[^}]+\}/g, '').replace(/\s+/g, ' ').trim();
|
|
84
|
+
headings.push({
|
|
85
|
+
level,
|
|
86
|
+
text,
|
|
87
|
+
line: lineNumberAtOffset(fileContent, hMatch.index),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// 4. Links: <Link to="..."> or <a href="...">
|
|
91
|
+
const linkRegex = /<(?:Link|NavLink)\b([^>]*?)>([\s\S]*?)<\/(?:Link|NavLink)>/gi;
|
|
92
|
+
let lMatch;
|
|
93
|
+
while ((lMatch = linkRegex.exec(fileContent)) !== null) {
|
|
94
|
+
const attrs = lMatch[1] ?? '';
|
|
95
|
+
const to = getAttr(attrs, 'to') || getAttr(attrs, 'href');
|
|
96
|
+
if (to) {
|
|
97
|
+
const anchorText = (lMatch[2] ?? '').replace(/<[^>]+>/g, '').trim();
|
|
98
|
+
links.push({
|
|
99
|
+
toUrl: to,
|
|
100
|
+
anchorText,
|
|
101
|
+
kind: to.startsWith('http://') || to.startsWith('https://') ? 'external' : 'internal',
|
|
102
|
+
rel: getAttr(attrs, 'rel'),
|
|
103
|
+
line: lineNumberAtOffset(fileContent, lMatch.index),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const aRegex = /<a\b([^>]*?)>([\s\S]*?)<\/a>/gi;
|
|
108
|
+
let aMatch;
|
|
109
|
+
while ((aMatch = aRegex.exec(fileContent)) !== null) {
|
|
110
|
+
const attrs = aMatch[1] ?? '';
|
|
111
|
+
const href = getAttr(attrs, 'href');
|
|
112
|
+
if (href) {
|
|
113
|
+
const anchorText = (aMatch[2] ?? '').replace(/<[^>]+>/g, '').trim();
|
|
114
|
+
links.push({
|
|
115
|
+
toUrl: href,
|
|
116
|
+
anchorText,
|
|
117
|
+
kind: href.startsWith('http://') || href.startsWith('https://') ? 'external' : 'internal',
|
|
118
|
+
rel: getAttr(attrs, 'rel'),
|
|
119
|
+
line: lineNumberAtOffset(fileContent, aMatch.index),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
route,
|
|
125
|
+
title,
|
|
126
|
+
description,
|
|
127
|
+
canonical,
|
|
128
|
+
robots,
|
|
129
|
+
htmlLang,
|
|
130
|
+
headings,
|
|
131
|
+
openGraph,
|
|
132
|
+
hreflang,
|
|
133
|
+
images,
|
|
134
|
+
links,
|
|
135
|
+
jsonLdItems,
|
|
136
|
+
isDynamic,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=remix-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remix-parser.js","sourceRoot":"","sources":["../../src/parsers/remix-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RobotsInfo {
|
|
2
|
+
readonly exists: boolean;
|
|
3
|
+
readonly filePath: string | null;
|
|
4
|
+
readonly hasUserAgent: boolean;
|
|
5
|
+
readonly sitemapUrls: ReadonlyArray<string>;
|
|
6
|
+
readonly disallowPaths: ReadonlyArray<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface SitemapInfo {
|
|
9
|
+
readonly exists: boolean;
|
|
10
|
+
readonly filePath: string | null;
|
|
11
|
+
readonly routePaths: ReadonlySet<string>;
|
|
12
|
+
}
|
|
13
|
+
export declare function parseRobotsFiles(filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): RobotsInfo;
|
|
14
|
+
export declare function parseSitemapFiles(filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): SitemapInfo;
|
|
15
|
+
//# sourceMappingURL=robots-sitemap-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots-sitemap-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/robots-sitemap-parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,UAAU,CAmDZ;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,WAAW,CAqDb"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export function parseRobotsFiles(filePaths, contentsByPath) {
|
|
2
|
+
const robotsPath = filePaths.find((p) => p === 'public/robots.txt' ||
|
|
3
|
+
p === 'robots.txt' ||
|
|
4
|
+
p === 'src/app/robots.ts' ||
|
|
5
|
+
p === 'src/app/robots.js' ||
|
|
6
|
+
p === 'app/robots.ts' ||
|
|
7
|
+
p === 'app/robots.js');
|
|
8
|
+
if (!robotsPath) {
|
|
9
|
+
return {
|
|
10
|
+
exists: false,
|
|
11
|
+
filePath: null,
|
|
12
|
+
hasUserAgent: false,
|
|
13
|
+
sitemapUrls: [],
|
|
14
|
+
disallowPaths: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const content = contentsByPath.get(robotsPath) ?? '';
|
|
18
|
+
const sitemapUrls = [];
|
|
19
|
+
const disallowPaths = [];
|
|
20
|
+
let hasUserAgent = false;
|
|
21
|
+
if (robotsPath.endsWith('.txt')) {
|
|
22
|
+
for (const line of content.split(/\r?\n/)) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (/^user-agent:/i.test(trimmed)) {
|
|
25
|
+
hasUserAgent = true;
|
|
26
|
+
}
|
|
27
|
+
else if (/^sitemap:/i.test(trimmed)) {
|
|
28
|
+
sitemapUrls.push(trimmed.replace(/^sitemap:\s*/i, '').trim());
|
|
29
|
+
}
|
|
30
|
+
else if (/^disallow:/i.test(trimmed)) {
|
|
31
|
+
const p = trimmed.replace(/^disallow:\s*/i, '').trim();
|
|
32
|
+
if (p)
|
|
33
|
+
disallowPaths.push(p);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
hasUserAgent = /userAgent/i.test(content);
|
|
39
|
+
const smMatch = /sitemap:\s*['"]([^'"]+)['"]/i.exec(content);
|
|
40
|
+
if (smMatch?.[1])
|
|
41
|
+
sitemapUrls.push(smMatch[1]);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
exists: true,
|
|
45
|
+
filePath: robotsPath,
|
|
46
|
+
hasUserAgent,
|
|
47
|
+
sitemapUrls,
|
|
48
|
+
disallowPaths,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function parseSitemapFiles(filePaths, contentsByPath) {
|
|
52
|
+
const sitemapPath = filePaths.find((p) => p === 'public/sitemap.xml' ||
|
|
53
|
+
p === 'sitemap.xml' ||
|
|
54
|
+
p === 'src/app/sitemap.ts' ||
|
|
55
|
+
p === 'src/app/sitemap.js' ||
|
|
56
|
+
p === 'app/sitemap.ts' ||
|
|
57
|
+
p === 'app/sitemap.js');
|
|
58
|
+
if (!sitemapPath) {
|
|
59
|
+
return {
|
|
60
|
+
exists: false,
|
|
61
|
+
filePath: null,
|
|
62
|
+
routePaths: new Set(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const content = contentsByPath.get(sitemapPath) ?? '';
|
|
66
|
+
const routePaths = new Set();
|
|
67
|
+
const locRegex = /<loc>\s*([^<\s]+)\s*<\/loc>/gi;
|
|
68
|
+
let m;
|
|
69
|
+
while ((m = locRegex.exec(content)) !== null) {
|
|
70
|
+
const rawUrl = m[1] ?? '';
|
|
71
|
+
try {
|
|
72
|
+
const u = new URL(rawUrl);
|
|
73
|
+
routePaths.add(u.pathname || '/');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
if (rawUrl.startsWith('/'))
|
|
77
|
+
routePaths.add(rawUrl);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const urlPropRegex = /url:\s*[`'"]([^`'"]+)[`'"]/g;
|
|
81
|
+
while ((m = urlPropRegex.exec(content)) !== null) {
|
|
82
|
+
const rawUrl = m[1] ?? '';
|
|
83
|
+
try {
|
|
84
|
+
const u = new URL(rawUrl);
|
|
85
|
+
routePaths.add(u.pathname || '/');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
const slashIdx = rawUrl.indexOf('/');
|
|
89
|
+
if (slashIdx >= 0) {
|
|
90
|
+
routePaths.add(rawUrl.slice(slashIdx));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
exists: true,
|
|
96
|
+
filePath: sitemapPath,
|
|
97
|
+
routePaths,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=robots-sitemap-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"robots-sitemap-parser.js","sourceRoot":"","sources":["../../src/parsers/robots-sitemap-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RouterKind } from '@seo-partner/core';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes framework-specific route file names and path declarations
|
|
4
|
+
* into a unified standard route pattern:
|
|
5
|
+
* - Dynamic parameter: `:param`
|
|
6
|
+
* - Catch-all parameter: `*rest`
|
|
7
|
+
* - Root path: `/`
|
|
8
|
+
* - Slashes: single leading slash, no trailing slash (unless root `/`)
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeRoutePattern(rawPath: string, routerKind?: RouterKind): string;
|
|
11
|
+
//# sourceMappingURL=route-normalizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-normalizer.d.ts","sourceRoot":"","sources":["../../src/parsers/route-normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,MAAM,CA2DtF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes framework-specific route file names and path declarations
|
|
3
|
+
* into a unified standard route pattern:
|
|
4
|
+
* - Dynamic parameter: `:param`
|
|
5
|
+
* - Catch-all parameter: `*rest`
|
|
6
|
+
* - Root path: `/`
|
|
7
|
+
* - Slashes: single leading slash, no trailing slash (unless root `/`)
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeRoutePattern(rawPath, routerKind) {
|
|
10
|
+
let cleaned = rawPath.replace(/\\/g, '/').trim();
|
|
11
|
+
// Strip leading folder roots if passed as full file paths
|
|
12
|
+
cleaned = cleaned.replace(/^(?:src\/)?(?:pages|routes|app\/routes)\//i, '');
|
|
13
|
+
// Strip file extensions if present (.astro, .vue, .svelte, .tsx, .jsx, .md, .mdx, .html, .php)
|
|
14
|
+
cleaned = cleaned.replace(/\.(astro|vue|svelte|tsx|jsx|ts|js|md|mdx|html|php)$/i, '');
|
|
15
|
+
// Strip route file conventions (e.g. Next.js page, SvelteKit +page, Remix route, index)
|
|
16
|
+
cleaned = cleaned.replace(/(?:^|\/)(?:page|\+page|route|index|_index)$/i, '');
|
|
17
|
+
if (routerKind === 'remix') {
|
|
18
|
+
// Remix flat route conventions:
|
|
19
|
+
// "blog.$slug" -> "blog/:slug"
|
|
20
|
+
// "docs.$" -> "docs/*"
|
|
21
|
+
// "$slug" -> ":slug"
|
|
22
|
+
// "$" -> "*"
|
|
23
|
+
cleaned = cleaned.replace(/\.\$([a-zA-Z0-9_]+)/g, '/:$1');
|
|
24
|
+
cleaned = cleaned.replace(/\.\$/g, '/*');
|
|
25
|
+
cleaned = cleaned.replace(/^\$([a-zA-Z0-9_]+)/g, ':$1');
|
|
26
|
+
cleaned = cleaned.replace(/^\$$/g, '*');
|
|
27
|
+
cleaned = cleaned.replace(/\./g, '/');
|
|
28
|
+
}
|
|
29
|
+
if (routerKind === 'django') {
|
|
30
|
+
// Django re_path named groups: (?P<slug>[-\w]+) -> :slug (run before <param> replacement)
|
|
31
|
+
cleaned = cleaned.replace(/\(\?P<([a-zA-Z0-9_]+)>[^)]+\)/g, ':$1');
|
|
32
|
+
// Django path converters: <int:id>, <slug:slug>, <str:username>, <uuid:pk>, <path:subpath>
|
|
33
|
+
cleaned = cleaned.replace(/<path:([a-zA-Z0-9_]+)>/g, '*$1');
|
|
34
|
+
cleaned = cleaned.replace(/<[a-zA-Z0-9_]+:([a-zA-Z0-9_]+)>/g, ':$1');
|
|
35
|
+
cleaned = cleaned.replace(/<([a-zA-Z0-9_]+)>/g, ':$1');
|
|
36
|
+
// Strip regex markers ^ and $
|
|
37
|
+
cleaned = cleaned.replace(/^\^/, '').replace(/\$$/, '');
|
|
38
|
+
}
|
|
39
|
+
// Next.js, Astro, Nuxt, SvelteKit brackets:
|
|
40
|
+
// Catch-all: [[...rest]] or [...rest] -> *rest
|
|
41
|
+
cleaned = cleaned.replace(/\[\[\.\.\.([a-zA-Z0-9_]+)\]\]/g, '*$1');
|
|
42
|
+
cleaned = cleaned.replace(/\[\.\.\.([a-zA-Z0-9_]+)\]/g, '*$1');
|
|
43
|
+
// Single dynamic: [param] -> :param
|
|
44
|
+
cleaned = cleaned.replace(/\[([a-zA-Z0-9_]+)\]/g, ':$1');
|
|
45
|
+
// Nuxt 3 catch-all with triple dots or just [...slug]
|
|
46
|
+
cleaned = cleaned.replace(/\[\.\.\.\]/g, '*');
|
|
47
|
+
// Remove parenthesis group folders: e.g. Next.js / Astro (marketing) -> ignored
|
|
48
|
+
cleaned = cleaned.replace(/\([a-zA-Z0-9_-]+\)\/?/g, '');
|
|
49
|
+
// Normalize slashes
|
|
50
|
+
cleaned = cleaned.replace(/\/+/g, '/');
|
|
51
|
+
if (!cleaned.startsWith('/')) {
|
|
52
|
+
cleaned = `/${cleaned}`;
|
|
53
|
+
}
|
|
54
|
+
if (cleaned.length > 1 && cleaned.endsWith('/')) {
|
|
55
|
+
cleaned = cleaned.slice(0, -1);
|
|
56
|
+
}
|
|
57
|
+
return cleaned || '/';
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=route-normalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-normalizer.js","sourceRoot":"","sources":["../../src/parsers/route-normalizer.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,UAAuB;IAC5E,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAEjD,0DAA0D;IAC1D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,4CAA4C,EAAE,EAAE,CAAC,CAAC;IAE5E,+FAA+F;IAC/F,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,sDAAsD,EAAE,EAAE,CAAC,CAAC;IAEtF,wFAAwF;IACxF,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,8CAA8C,EAAE,EAAE,CAAC,CAAC;IAE9E,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,gCAAgC;QAChC,+BAA+B;QAC/B,uBAAuB;QACvB,qBAAqB;QACrB,aAAa;QACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACzC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,0FAA0F;QAC1F,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACnE,2FAA2F;QAC3F,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;QACrE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;QACvD,8BAA8B;QAC9B,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,4CAA4C;IAC5C,+CAA+C;IAC/C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;IACnE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;IAC/D,oCAAoC;IACpC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;IAEzD,sDAAsD;IACtD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAE9C,gFAAgF;IAChF,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC,CAAC;IAExD,oBAAoB;IACpB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC1B,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,OAAO,IAAI,GAAG,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/svelte-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAKnB,MAAM,mBAAmB,CAAC;AAgB3B,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,kBAAkB,CA2IpB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
function lineNumberAtOffset(content, offset) {
|
|
2
|
+
let line = 1;
|
|
3
|
+
for (let i = 0; i < offset && i < content.length; i += 1) {
|
|
4
|
+
if (content.charCodeAt(i) === 10)
|
|
5
|
+
line += 1;
|
|
6
|
+
}
|
|
7
|
+
return line;
|
|
8
|
+
}
|
|
9
|
+
function getAttr(attrsRaw, attrName) {
|
|
10
|
+
const re = new RegExp(`\\b${attrName}\\s*=\\s*["']([^"']*)["']`, 'i');
|
|
11
|
+
const m = re.exec(attrsRaw);
|
|
12
|
+
return m ? (m[1] ?? null) : null;
|
|
13
|
+
}
|
|
14
|
+
export function parseSvelteComponent(route, fileContent) {
|
|
15
|
+
let title = null;
|
|
16
|
+
let description = null;
|
|
17
|
+
let canonical = null;
|
|
18
|
+
let robots = null;
|
|
19
|
+
let htmlLang = undefined;
|
|
20
|
+
let isDynamic = false;
|
|
21
|
+
const openGraph = {};
|
|
22
|
+
const hreflang = [];
|
|
23
|
+
const headings = [];
|
|
24
|
+
const images = [];
|
|
25
|
+
const links = [];
|
|
26
|
+
const jsonLdItems = [];
|
|
27
|
+
// 1. <html lang="..."> (common in +layout.svelte or root template)
|
|
28
|
+
const htmlTagMatch = /<html\b([^>]*?)>/i.exec(fileContent);
|
|
29
|
+
if (htmlTagMatch) {
|
|
30
|
+
const langVal = getAttr(htmlTagMatch[1] ?? '', 'lang');
|
|
31
|
+
if (langVal) {
|
|
32
|
+
htmlLang = {
|
|
33
|
+
value: langVal.trim(),
|
|
34
|
+
line: lineNumberAtOffset(fileContent, htmlTagMatch.index),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 2. <svelte:head> block
|
|
39
|
+
const svelteHeadMatch = /<svelte:head>([\s\S]*?)<\/svelte:head>/i.exec(fileContent);
|
|
40
|
+
const headContent = svelteHeadMatch && svelteHeadMatch[1] ? svelteHeadMatch[1] : '';
|
|
41
|
+
const headOffset = svelteHeadMatch ? svelteHeadMatch.index : 0;
|
|
42
|
+
if (headContent) {
|
|
43
|
+
// Title inside <svelte:head>
|
|
44
|
+
const titleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(headContent);
|
|
45
|
+
if (titleMatch) {
|
|
46
|
+
const rawVal = (titleMatch[1] ?? '').trim();
|
|
47
|
+
const line = lineNumberAtOffset(fileContent, headOffset + titleMatch.index);
|
|
48
|
+
if (/\{[^}]+\}/.test(rawVal)) {
|
|
49
|
+
title = { value: '[Valeur dynamique]', line };
|
|
50
|
+
isDynamic = true;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
title = { value: rawVal.replace(/\s+/g, ' '), line };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Meta tags inside <svelte:head>
|
|
57
|
+
const metaRegex = /<meta\b([^>]*?)\/?>/gi;
|
|
58
|
+
let mMatch;
|
|
59
|
+
while ((mMatch = metaRegex.exec(headContent)) !== null) {
|
|
60
|
+
const attrs = mMatch[1] ?? '';
|
|
61
|
+
const line = lineNumberAtOffset(fileContent, headOffset + mMatch.index);
|
|
62
|
+
const nameVal = getAttr(attrs, 'name')?.toLowerCase();
|
|
63
|
+
const propVal = getAttr(attrs, 'property')?.toLowerCase();
|
|
64
|
+
const contentVal = getAttr(attrs, 'content');
|
|
65
|
+
if (nameVal === 'description' && contentVal !== null && !description) {
|
|
66
|
+
description = { value: contentVal, line };
|
|
67
|
+
}
|
|
68
|
+
else if (nameVal === 'robots' && contentVal !== null && !robots) {
|
|
69
|
+
robots = { value: contentVal, line };
|
|
70
|
+
}
|
|
71
|
+
else if (propVal?.startsWith('og:') && contentVal !== null) {
|
|
72
|
+
openGraph[propVal.slice(3)] = contentVal;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Canonical link inside <svelte:head>
|
|
76
|
+
const linkRegex = /<link\b([^>]*?)\/?>/gi;
|
|
77
|
+
let lMatch;
|
|
78
|
+
while ((lMatch = linkRegex.exec(headContent)) !== null) {
|
|
79
|
+
const attrs = lMatch[1] ?? '';
|
|
80
|
+
const line = lineNumberAtOffset(fileContent, headOffset + lMatch.index);
|
|
81
|
+
const relVal = getAttr(attrs, 'rel')?.toLowerCase();
|
|
82
|
+
const hrefVal = getAttr(attrs, 'href');
|
|
83
|
+
if (relVal === 'canonical' && hrefVal !== null && !canonical) {
|
|
84
|
+
canonical = { value: hrefVal, line };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Fallback direct <title> if outside <svelte:head>
|
|
89
|
+
if (!title) {
|
|
90
|
+
const directTitleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(fileContent);
|
|
91
|
+
if (directTitleMatch) {
|
|
92
|
+
const rawVal = (directTitleMatch[1] ?? '').trim();
|
|
93
|
+
const line = lineNumberAtOffset(fileContent, directTitleMatch.index);
|
|
94
|
+
if (/\{[^}]+\}/.test(rawVal)) {
|
|
95
|
+
title = { value: '[Valeur dynamique]', line };
|
|
96
|
+
isDynamic = true;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
title = { value: rawVal.replace(/\s+/g, ' '), line };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// 3. Headings: <h1> to <h6>
|
|
104
|
+
const headingRegex = /<h([1-6])\b[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
105
|
+
let hMatch;
|
|
106
|
+
while ((hMatch = headingRegex.exec(fileContent)) !== null) {
|
|
107
|
+
const level = Number(hMatch[1]);
|
|
108
|
+
const text = (hMatch[2] ?? '').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
|
109
|
+
headings.push({
|
|
110
|
+
level,
|
|
111
|
+
text,
|
|
112
|
+
line: lineNumberAtOffset(fileContent, hMatch.index),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// 4. Links: <a href="...">
|
|
116
|
+
const aRegex = /<a\b([^>]*?)>([\s\S]*?)<\/a>/gi;
|
|
117
|
+
let aMatch;
|
|
118
|
+
while ((aMatch = aRegex.exec(fileContent)) !== null) {
|
|
119
|
+
const attrs = aMatch[1] ?? '';
|
|
120
|
+
const href = getAttr(attrs, 'href');
|
|
121
|
+
if (href) {
|
|
122
|
+
const anchorText = (aMatch[2] ?? '').replace(/<[^>]+>/g, '').trim();
|
|
123
|
+
links.push({
|
|
124
|
+
toUrl: href,
|
|
125
|
+
anchorText,
|
|
126
|
+
kind: href.startsWith('http://') || href.startsWith('https://') ? 'external' : 'internal',
|
|
127
|
+
rel: getAttr(attrs, 'rel'),
|
|
128
|
+
line: lineNumberAtOffset(fileContent, aMatch.index),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
route,
|
|
134
|
+
title,
|
|
135
|
+
description,
|
|
136
|
+
canonical,
|
|
137
|
+
robots,
|
|
138
|
+
htmlLang,
|
|
139
|
+
headings,
|
|
140
|
+
openGraph,
|
|
141
|
+
hreflang,
|
|
142
|
+
images,
|
|
143
|
+
links,
|
|
144
|
+
jsonLdItems,
|
|
145
|
+
isDynamic,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=svelte-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte-parser.js","sourceRoot":"","sources":["../../src/parsers/svelte-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DiscoveredRoute, ExtractedRouteData, RenderingMode } from '@seo-partner/core';
|
|
2
|
+
export declare function detectRenderingModeFromTsx(sourceText: string): RenderingMode;
|
|
3
|
+
export declare function parseTsxRouteFile(route: DiscoveredRoute, fileContent: string): ExtractedRouteData;
|
|
4
|
+
//# sourceMappingURL=tsx-ast-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsx-ast-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/tsx-ast-parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAKlB,aAAa,EACd,MAAM,mBAAmB,CAAC;AAE3B,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAS5E;AA4GD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,kBAAkB,CA+QpB"}
|