@seo-partner/adapters 0.0.0-stage → 1.0.0
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/astro-adapter.d.ts +13 -0
- package/dist/astro-adapter.d.ts.map +1 -0
- package/dist/astro-adapter.js +95 -0
- package/dist/astro-adapter.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 +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.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 +13 -0
- package/dist/nuxt-adapter.d.ts.map +1 -0
- package/dist/nuxt-adapter.js +88 -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/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/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 +13 -0
- package/dist/sveltekit-adapter.d.ts.map +1 -0
- package/dist/sveltekit-adapter.js +89 -0
- package/dist/sveltekit-adapter.js.map +1 -0
- package/package.json +23 -3
- package/src/astro-adapter.ts +126 -0
- package/src/contract.ts +40 -0
- package/src/detector.ts +46 -0
- package/src/django-adapter.ts +173 -0
- package/src/index.ts +23 -0
- package/src/nextjs-adapter.ts +116 -0
- package/src/nuxt-adapter.ts +118 -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/remix-adapter.ts +123 -0
- package/src/static-adapter.ts +62 -0
- package/src/sveltekit-adapter.ts +119 -0
- package/tsconfig.json +9 -0
- package/README.md +0 -3
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseStaticHtmlOrMarkdown } from './html-parser.js';
|
|
4
|
+
function lineNumberAtOffset(content, offset) {
|
|
5
|
+
let line = 1;
|
|
6
|
+
for (let i = 0; i < offset && i < content.length; i += 1) {
|
|
7
|
+
if (content.charCodeAt(i) === 10)
|
|
8
|
+
line += 1;
|
|
9
|
+
}
|
|
10
|
+
return line;
|
|
11
|
+
}
|
|
12
|
+
function readProjectFileSafe(projectRoot, relOrAbsPath) {
|
|
13
|
+
try {
|
|
14
|
+
const full = path.isAbsolute(relOrAbsPath)
|
|
15
|
+
? relOrAbsPath
|
|
16
|
+
: path.join(projectRoot, relOrAbsPath);
|
|
17
|
+
if (fs.existsSync(full) && fs.statSync(full).isFile()) {
|
|
18
|
+
return fs.readFileSync(full, 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function extractBladeMetadata(projectRoot, route, fileContent) {
|
|
27
|
+
const base = parseStaticHtmlOrMarkdown({ ...route, routerKind: 'static-html' }, fileContent);
|
|
28
|
+
let title = base.title;
|
|
29
|
+
let description = base.description;
|
|
30
|
+
let canonical = base.canonical;
|
|
31
|
+
let robots = base.robots;
|
|
32
|
+
let htmlLang = base.htmlLang;
|
|
33
|
+
// 1. Check @section('title', '...') or @section('title')...@endsection
|
|
34
|
+
const inlineTitleMatch = /@section\s*\(\s*['"]title['"]\s*,\s*['"]([^'"]*)['"]\s*\)/i.exec(fileContent);
|
|
35
|
+
if (inlineTitleMatch) {
|
|
36
|
+
title = {
|
|
37
|
+
value: (inlineTitleMatch[1] ?? '').trim(),
|
|
38
|
+
line: lineNumberAtOffset(fileContent, inlineTitleMatch.index),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const blockTitleMatch = /@section\s*\(\s*['"]title['"]\s*\)([\s\S]*?)@(?:endsection|stop)/i.exec(fileContent);
|
|
43
|
+
if (blockTitleMatch) {
|
|
44
|
+
title = {
|
|
45
|
+
value: (blockTitleMatch[1] ?? '').replace(/<[^>]+>/g, '').trim(),
|
|
46
|
+
line: lineNumberAtOffset(fileContent, blockTitleMatch.index),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// 2. Check @section('description', '...')
|
|
51
|
+
const inlineDescMatch = /@section\s*\(\s*['"](?:description|meta_description)['"]\s*,\s*['"]([^'"]*)['"]\s*\)/i.exec(fileContent);
|
|
52
|
+
if (inlineDescMatch) {
|
|
53
|
+
description = {
|
|
54
|
+
value: (inlineDescMatch[1] ?? '').trim(),
|
|
55
|
+
line: lineNumberAtOffset(fileContent, inlineDescMatch.index),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// 3. Resolve @extends('layouts.app')
|
|
59
|
+
const extendsMatch = /@extends\s*\(\s*['"]([^'"]+)['"]\s*\)/i.exec(fileContent);
|
|
60
|
+
if (extendsMatch?.[1]) {
|
|
61
|
+
const layoutRel = `resources/views/${extendsMatch[1].replace(/\./g, '/')}.blade.php`;
|
|
62
|
+
const layoutContent = readProjectFileSafe(projectRoot, layoutRel);
|
|
63
|
+
if (layoutContent) {
|
|
64
|
+
const layoutParsed = parseStaticHtmlOrMarkdown({ ...route, filePath: layoutRel, routerKind: 'static-html' }, layoutContent);
|
|
65
|
+
if (htmlLang === undefined && layoutParsed.htmlLang !== undefined) {
|
|
66
|
+
htmlLang = layoutParsed.htmlLang;
|
|
67
|
+
}
|
|
68
|
+
if (!title) {
|
|
69
|
+
const yieldTitleDefault = /@yield\s*\(\s*['"]title['"]\s*,\s*['"]([^'"]+)['"]\s*\)/i.exec(layoutContent);
|
|
70
|
+
if (yieldTitleDefault?.[1]) {
|
|
71
|
+
title = {
|
|
72
|
+
value: yieldTitleDefault[1].trim(),
|
|
73
|
+
line: lineNumberAtOffset(layoutContent, yieldTitleDefault.index),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
else if (layoutParsed.title &&
|
|
77
|
+
!layoutParsed.title.value.includes('@yield')) {
|
|
78
|
+
title = layoutParsed.title;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (!description) {
|
|
82
|
+
const yieldDescDefault = /@yield\s*\(\s*['"]description['"]\s*,\s*['"]([^'"]+)['"]\s*\)/i.exec(layoutContent);
|
|
83
|
+
if (yieldDescDefault?.[1] && yieldDescDefault[1].trim().length > 0) {
|
|
84
|
+
description = {
|
|
85
|
+
value: yieldDescDefault[1].trim(),
|
|
86
|
+
line: lineNumberAtOffset(layoutContent, yieldDescDefault.index),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
else if (layoutParsed.description &&
|
|
90
|
+
!layoutParsed.description.value.includes('@yield') &&
|
|
91
|
+
layoutParsed.description.value.trim().length > 0) {
|
|
92
|
+
description = layoutParsed.description;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!canonical && layoutParsed.canonical) {
|
|
96
|
+
canonical = layoutParsed.canonical;
|
|
97
|
+
}
|
|
98
|
+
if (!robots && layoutParsed.robots) {
|
|
99
|
+
robots = layoutParsed.robots;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...base,
|
|
105
|
+
route,
|
|
106
|
+
title,
|
|
107
|
+
description,
|
|
108
|
+
canonical,
|
|
109
|
+
robots,
|
|
110
|
+
...(htmlLang !== undefined ? { htmlLang } : {}),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function extractTwigMetadata(projectRoot, route, fileContent) {
|
|
114
|
+
const base = parseStaticHtmlOrMarkdown({ ...route, routerKind: 'static-html' }, fileContent);
|
|
115
|
+
let title = base.title;
|
|
116
|
+
let description = base.description;
|
|
117
|
+
let canonical = base.canonical;
|
|
118
|
+
let robots = base.robots;
|
|
119
|
+
let htmlLang = base.htmlLang;
|
|
120
|
+
// 1. Check {% block title %}...{% endblock %}
|
|
121
|
+
const blockTitleMatch = /\{%\s*block\s+title\s*%\}[\s\S]*?\{%\s*endblock(?:\s+title)?\s*%\}/i.exec(fileContent);
|
|
122
|
+
if (blockTitleMatch) {
|
|
123
|
+
const inner = blockTitleMatch[0]
|
|
124
|
+
.replace(/^\{%\s*block\s+title\s*%\}/i, '')
|
|
125
|
+
.replace(/\{%\s*endblock(?:\s+title)?\s*%\}$/i, '')
|
|
126
|
+
.replace(/<[^>]+>/g, '')
|
|
127
|
+
.trim();
|
|
128
|
+
if (inner) {
|
|
129
|
+
title = {
|
|
130
|
+
value: inner,
|
|
131
|
+
line: lineNumberAtOffset(fileContent, blockTitleMatch.index),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// 2. Resolve {% extends 'base.html.twig' %}
|
|
136
|
+
const extendsMatch = /\{%\s*extends\s+['"]([^'"]+)['"]\s*%\}/i.exec(fileContent);
|
|
137
|
+
if (extendsMatch?.[1]) {
|
|
138
|
+
const parentRel = `templates/${extendsMatch[1].replace(/^\/+/, '')}`;
|
|
139
|
+
const parentContent = readProjectFileSafe(projectRoot, parentRel);
|
|
140
|
+
if (parentContent) {
|
|
141
|
+
const parentParsed = parseStaticHtmlOrMarkdown({ ...route, filePath: parentRel, routerKind: 'static-html' }, parentContent);
|
|
142
|
+
if (htmlLang === undefined && parentParsed.htmlLang !== undefined) {
|
|
143
|
+
htmlLang = parentParsed.htmlLang;
|
|
144
|
+
}
|
|
145
|
+
if (!title && parentParsed.title) {
|
|
146
|
+
const cleanedParentTitle = parentParsed.title.value
|
|
147
|
+
.replace(/\{%\s*block\s+title\s*%\}/i, '')
|
|
148
|
+
.replace(/\{%\s*endblock(?:\s+title)?\s*%\}/i, '')
|
|
149
|
+
.trim();
|
|
150
|
+
if (cleanedParentTitle) {
|
|
151
|
+
title = {
|
|
152
|
+
value: cleanedParentTitle,
|
|
153
|
+
line: parentParsed.title.line,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!description && parentParsed.description) {
|
|
158
|
+
description = parentParsed.description;
|
|
159
|
+
}
|
|
160
|
+
if (!canonical && parentParsed.canonical) {
|
|
161
|
+
canonical = parentParsed.canonical;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
...base,
|
|
167
|
+
route,
|
|
168
|
+
title,
|
|
169
|
+
description,
|
|
170
|
+
canonical,
|
|
171
|
+
robots,
|
|
172
|
+
...(htmlLang !== undefined ? { htmlLang } : {}),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function extractNativePhpMetadata(projectRoot, route, fileContent) {
|
|
176
|
+
const base = parseStaticHtmlOrMarkdown({ ...route, routerKind: 'static-html' }, fileContent);
|
|
177
|
+
let title = base.title;
|
|
178
|
+
let description = base.description;
|
|
179
|
+
let canonical = base.canonical;
|
|
180
|
+
let robots = base.robots;
|
|
181
|
+
let htmlLang = base.htmlLang;
|
|
182
|
+
// Check for PHP include/require of a header file (e.g. require_once __DIR__ . '/../includes/header.php';)
|
|
183
|
+
const includeRegex = /\b(?:include|include_once|require|require_once)\b[^;]*?['"]([^'"]+\.php)['"]/gi;
|
|
184
|
+
let m;
|
|
185
|
+
const currentFileDir = path.dirname(path.join(projectRoot, route.filePath));
|
|
186
|
+
while ((m = includeRegex.exec(fileContent)) !== null) {
|
|
187
|
+
const rawInc = (m[1] ?? '').replace(/^\/+/, '');
|
|
188
|
+
const candidateRelToDir = path.resolve(currentFileDir, rawInc);
|
|
189
|
+
const candidateRelToRoot = path.resolve(projectRoot, rawInc);
|
|
190
|
+
const incContent = readProjectFileSafe(projectRoot, candidateRelToDir) ??
|
|
191
|
+
readProjectFileSafe(projectRoot, candidateRelToRoot);
|
|
192
|
+
if (incContent) {
|
|
193
|
+
const incParsed = parseStaticHtmlOrMarkdown({ ...route, routerKind: 'static-html' }, incContent);
|
|
194
|
+
if (htmlLang === undefined && incParsed.htmlLang !== undefined) {
|
|
195
|
+
htmlLang = incParsed.htmlLang;
|
|
196
|
+
}
|
|
197
|
+
if (!title && incParsed.title) {
|
|
198
|
+
title = incParsed.title;
|
|
199
|
+
}
|
|
200
|
+
if (!description && incParsed.description) {
|
|
201
|
+
description = incParsed.description;
|
|
202
|
+
}
|
|
203
|
+
if (!canonical && incParsed.canonical) {
|
|
204
|
+
canonical = incParsed.canonical;
|
|
205
|
+
}
|
|
206
|
+
if (!robots && incParsed.robots) {
|
|
207
|
+
robots = incParsed.robots;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
...base,
|
|
213
|
+
route,
|
|
214
|
+
title,
|
|
215
|
+
description,
|
|
216
|
+
canonical,
|
|
217
|
+
robots,
|
|
218
|
+
...(htmlLang !== undefined ? { htmlLang } : {}),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
export function parsePhpOrTemplateFile(projectRoot, route, fileContent) {
|
|
222
|
+
if (route.filePath.endsWith('.blade.php') || route.routerKind === 'laravel') {
|
|
223
|
+
return extractBladeMetadata(projectRoot, route, fileContent);
|
|
224
|
+
}
|
|
225
|
+
if (route.filePath.endsWith('.html.twig') || route.routerKind === 'symfony') {
|
|
226
|
+
return extractTwigMetadata(projectRoot, route, fileContent);
|
|
227
|
+
}
|
|
228
|
+
return extractNativePhpMetadata(projectRoot, route, fileContent);
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=php-template-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"php-template-parser.js","sourceRoot":"","sources":["../../src/parsers/php-template-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DjangoDiscoveredPattern {
|
|
2
|
+
readonly pattern: string;
|
|
3
|
+
readonly rawPattern: string;
|
|
4
|
+
readonly viewName: string;
|
|
5
|
+
readonly lineNumber: number;
|
|
6
|
+
}
|
|
7
|
+
export interface DjangoViewTemplateMapping {
|
|
8
|
+
readonly viewName: string;
|
|
9
|
+
readonly templateName: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Statically parses a Django urls.py file to extract route declarations without executing Python.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseDjangoUrls(urlsPyContent: string): ReadonlyArray<DjangoDiscoveredPattern>;
|
|
15
|
+
/**
|
|
16
|
+
* Statically parses a Django views.py file to extract view -> template_name mappings.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseDjangoViews(viewsPyContent: string): ReadonlyArray<DjangoViewTemplateMapping>;
|
|
19
|
+
//# sourceMappingURL=python-url-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-url-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/python-url-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAUD;;GAEG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAAC,uBAAuB,CAAC,CA6B7F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,yBAAyB,CAAC,CA+BjG"}
|
|
@@ -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"}
|