@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Firinze Gbènito Dossou (Leadbooster)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface MetadataFields {
|
|
2
|
+
title?: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
canonical?: string;
|
|
5
|
+
openGraph?: {
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
siteName?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface AstMergeResult {
|
|
13
|
+
mergedCode: string;
|
|
14
|
+
modified: boolean;
|
|
15
|
+
hasComplexDynamicLogic: boolean;
|
|
16
|
+
isClientComponent: boolean;
|
|
17
|
+
changes: Array<{
|
|
18
|
+
field: string;
|
|
19
|
+
before?: string | null;
|
|
20
|
+
after: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export declare function mergePageMetadataInPlace(sourceCode: string, newMeta: MetadataFields): AstMergeResult;
|
|
24
|
+
//# sourceMappingURL=ast-merger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-merger.d.ts","sourceRoot":"","sources":["../src/ast-merger.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,sBAAsB,EAAE,OAAO,CAAC;IAChC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,GACtB,cAAc,CA+FhB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export function mergePageMetadataInPlace(sourceCode, newMeta) {
|
|
2
|
+
const isClientComponent = /^\s*['"]use client['"]/m.test(sourceCode);
|
|
3
|
+
const hasGenerateMetadata = /\bexport\s+(async\s+)?function\s+generateMetadata\b/.test(sourceCode);
|
|
4
|
+
if (hasGenerateMetadata) {
|
|
5
|
+
return {
|
|
6
|
+
mergedCode: sourceCode,
|
|
7
|
+
modified: false,
|
|
8
|
+
hasComplexDynamicLogic: true,
|
|
9
|
+
isClientComponent,
|
|
10
|
+
changes: [],
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const changes = [];
|
|
14
|
+
// Match existing `export const metadata = { ... }` or `export const metadata: Metadata = { ... }`
|
|
15
|
+
const metadataRegex = /(export\s+const\s+metadata(?:\s*:\s*\w+)?\s*=\s*\{)([\s\S]*?)(\};?)/m;
|
|
16
|
+
const match = metadataRegex.exec(sourceCode);
|
|
17
|
+
if (match) {
|
|
18
|
+
const prefix = match[1] ?? '';
|
|
19
|
+
let body = match[2] ?? '';
|
|
20
|
+
const suffix = match[3] ?? '';
|
|
21
|
+
// Check title
|
|
22
|
+
if (newMeta.title && !/\btitle\s*:/m.test(body)) {
|
|
23
|
+
const val = newMeta.title;
|
|
24
|
+
body = `\n title: '${val.replace(/'/g, "\\'")}',` + body;
|
|
25
|
+
changes.push({ field: 'title', before: null, after: val });
|
|
26
|
+
}
|
|
27
|
+
// Check description
|
|
28
|
+
if (newMeta.description && !/\bdescription\s*:/m.test(body)) {
|
|
29
|
+
const val = newMeta.description;
|
|
30
|
+
body = `\n description: '${val.replace(/'/g, "\\'")}',` + body;
|
|
31
|
+
changes.push({ field: 'description', before: null, after: val });
|
|
32
|
+
}
|
|
33
|
+
// Check alternates / canonical
|
|
34
|
+
if (newMeta.canonical && !/\balternates\s*:/m.test(body)) {
|
|
35
|
+
const val = newMeta.canonical;
|
|
36
|
+
body = body + `\n alternates: {\n canonical: '${val}',\n },`;
|
|
37
|
+
changes.push({ field: 'alternates.canonical', before: null, after: val });
|
|
38
|
+
}
|
|
39
|
+
// Check openGraph
|
|
40
|
+
if (newMeta.openGraph && !/\bopenGraph\s*:/m.test(body)) {
|
|
41
|
+
const ogTitle = newMeta.openGraph.title ?? newMeta.title ?? '';
|
|
42
|
+
const ogDesc = newMeta.openGraph.description ?? newMeta.description ?? '';
|
|
43
|
+
body = body + `\n openGraph: {\n title: '${ogTitle.replace(/'/g, "\\'")}',\n description: '${ogDesc.replace(/'/g, "\\'")}',\n },`;
|
|
44
|
+
changes.push({ field: 'openGraph', before: null, after: ogTitle });
|
|
45
|
+
}
|
|
46
|
+
const mergedCode = sourceCode.replace(metadataRegex, `${prefix}${body}\n${suffix}`);
|
|
47
|
+
return {
|
|
48
|
+
mergedCode,
|
|
49
|
+
modified: changes.length > 0,
|
|
50
|
+
hasComplexDynamicLogic: false,
|
|
51
|
+
isClientComponent,
|
|
52
|
+
changes,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// No existing metadata block: create one
|
|
56
|
+
const entries = [];
|
|
57
|
+
if (newMeta.title) {
|
|
58
|
+
entries.push(` title: '${newMeta.title.replace(/'/g, "\\'")}',`);
|
|
59
|
+
changes.push({ field: 'title', before: null, after: newMeta.title });
|
|
60
|
+
}
|
|
61
|
+
if (newMeta.description) {
|
|
62
|
+
entries.push(` description: '${newMeta.description.replace(/'/g, "\\'")}',`);
|
|
63
|
+
changes.push({ field: 'description', before: null, after: newMeta.description });
|
|
64
|
+
}
|
|
65
|
+
if (newMeta.canonical) {
|
|
66
|
+
entries.push(` alternates: {\n canonical: '${newMeta.canonical}',\n },`);
|
|
67
|
+
changes.push({ field: 'alternates.canonical', before: null, after: newMeta.canonical });
|
|
68
|
+
}
|
|
69
|
+
if (newMeta.openGraph) {
|
|
70
|
+
const ogTitle = newMeta.openGraph.title ?? newMeta.title ?? '';
|
|
71
|
+
const ogDesc = newMeta.openGraph.description ?? newMeta.description ?? '';
|
|
72
|
+
entries.push(` openGraph: {\n title: '${ogTitle.replace(/'/g, "\\'")}',\n description: '${ogDesc.replace(/'/g, "\\'")}',\n },`);
|
|
73
|
+
changes.push({ field: 'openGraph', before: null, after: ogTitle });
|
|
74
|
+
}
|
|
75
|
+
const newExport = `\nexport const metadata = {\n${entries.join('\n')}\n};\n`;
|
|
76
|
+
const mergedCode = sourceCode + newExport;
|
|
77
|
+
return {
|
|
78
|
+
mergedCode,
|
|
79
|
+
modified: true,
|
|
80
|
+
hasComplexDynamicLogic: false,
|
|
81
|
+
isClientComponent,
|
|
82
|
+
changes,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=ast-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-merger.js","sourceRoot":"","sources":["../src/ast-merger.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,wBAAwB,CACtC,UAAkB,EAClB,OAAuB;IAEvB,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACrE,MAAM,mBAAmB,GAAG,qDAAqD,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEnG,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO;YACL,UAAU,EAAE,UAAU;YACtB,QAAQ,EAAE,KAAK;YACf,sBAAsB,EAAE,IAAI;YAC5B,iBAAiB;YACjB,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAoE,EAAE,CAAC;IAEpF,kGAAkG;IAClG,MAAM,aAAa,GAAG,sEAAsE,CAAC;IAC7F,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAE7C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAE9B,cAAc;QACd,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC;YAC1B,IAAI,GAAG,eAAe,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,oBAAoB;QACpB,IAAI,OAAO,CAAC,WAAW,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC;YAChC,IAAI,GAAG,qBAAqB,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,+BAA+B;QAC/B,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACzD,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC;YAC9B,IAAI,GAAG,IAAI,GAAG,sCAAsC,GAAG,UAAU,CAAC;YAClE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,kBAAkB;QAClB,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;YAC1E,IAAI,GAAG,IAAI,GAAG,iCAAiC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,yBAAyB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC;YAC1I,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,MAAM,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC;QACpF,OAAO;YACL,UAAU;YACV,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC;YAC5B,sBAAsB,EAAE,KAAK;YAC7B,iBAAiB;YACjB,OAAO;SACR,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,mBAAmB,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,oCAAoC,OAAO,CAAC,SAAS,UAAU,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,+BAA+B,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,yBAAyB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACxI,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,SAAS,GAAG,gCAAgC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;IAC7E,MAAM,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IAE1C,OAAO;QACL,UAAU;QACV,QAAQ,EAAE,IAAI;QACd,sBAAsB,EAAE,KAAK;QAC7B,iBAAiB;QACjB,OAAO;KACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DiscoveredRoute, ExtractedRouteData } from '@seo-partner/core';
|
|
2
|
+
import type { AdapterCapabilities, AdapterVersion, FrameworkAdapter } from './contract.js';
|
|
3
|
+
export declare class AstroAdapter implements FrameworkAdapter {
|
|
4
|
+
readonly id = "astro";
|
|
5
|
+
readonly name = "Astro";
|
|
6
|
+
readonly version: AdapterVersion;
|
|
7
|
+
readonly capabilities: AdapterCapabilities;
|
|
8
|
+
private readonly layoutResolver;
|
|
9
|
+
detect(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): Promise<boolean>;
|
|
10
|
+
discoverRoutes(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>, sitemapPaths: ReadonlySet<string>): Promise<ReadonlyArray<DiscoveredRoute>>;
|
|
11
|
+
extractRoute(projectRoot: string, route: DiscoveredRoute, fileContent: string): Promise<ExtractedRouteData>;
|
|
12
|
+
}
|
|
13
|
+
export interface AstroMetaFields {
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
canonical?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function patchAstroTemplate(sourceCode: string, meta: AstroMetaFields): string;
|
|
19
|
+
//# sourceMappingURL=astro-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"astro-adapter.d.ts","sourceRoot":"","sources":["../src/astro-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAK3F,qBAAa,YAAa,YAAW,gBAAgB;IACnD,SAAgB,EAAE,WAAW;IAC7B,SAAgB,IAAI,WAAW;IAC/B,SAAgB,OAAO,EAAE,cAAc,CAAS;IAChD,SAAgB,YAAY,EAAE,mBAAmB,CAQ/C;IAEF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IAE1C,MAAM,CACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,OAAO,CAAC,OAAO,CAAC;IAqBN,cAAc,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAoD7B,YAAY,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC;CAe/B;AAGD,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,eAAe,GACpB,MAAM,CAkBR"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { parseAstroOrMarkdown } from './parsers/astro-parser.js';
|
|
2
|
+
import { normalizeRoutePattern } from './parsers/route-normalizer.js';
|
|
3
|
+
import { LayoutResolver } from './parsers/layout-resolver.js';
|
|
4
|
+
export class AstroAdapter {
|
|
5
|
+
id = 'astro';
|
|
6
|
+
name = 'Astro';
|
|
7
|
+
version = '1.0';
|
|
8
|
+
capabilities = {
|
|
9
|
+
routes: true,
|
|
10
|
+
metadata: true,
|
|
11
|
+
headings: true,
|
|
12
|
+
internalLinks: true,
|
|
13
|
+
jsonLd: true,
|
|
14
|
+
images: true,
|
|
15
|
+
robotsAndSitemap: true,
|
|
16
|
+
};
|
|
17
|
+
layoutResolver = new LayoutResolver();
|
|
18
|
+
async detect(projectRoot, filePaths, contentsByPath) {
|
|
19
|
+
for (const filePath of filePaths) {
|
|
20
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
21
|
+
if (normalized === 'astro.config.mjs' ||
|
|
22
|
+
normalized === 'astro.config.ts' ||
|
|
23
|
+
normalized === 'astro.config.js' ||
|
|
24
|
+
normalized === 'astro.config.cjs') {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (normalized === 'package.json') {
|
|
28
|
+
const content = contentsByPath.get(filePath) ?? '';
|
|
29
|
+
if (content.includes('"astro"') || content.includes('"@astrojs/')) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
async discoverRoutes(projectRoot, filePaths, contentsByPath, sitemapPaths) {
|
|
37
|
+
const routes = [];
|
|
38
|
+
// Pre-cache layouts from src/layouts/**
|
|
39
|
+
for (const filePath of filePaths) {
|
|
40
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
41
|
+
if (normalized.startsWith('src/layouts/') && normalized.endsWith('.astro')) {
|
|
42
|
+
const content = contentsByPath.get(filePath) ?? '';
|
|
43
|
+
const parsed = parseAstroOrMarkdown({
|
|
44
|
+
pattern: '',
|
|
45
|
+
filePath,
|
|
46
|
+
routerKind: 'astro',
|
|
47
|
+
renderingMode: 'static',
|
|
48
|
+
isPrivate: false,
|
|
49
|
+
inSitemap: false,
|
|
50
|
+
}, content);
|
|
51
|
+
this.layoutResolver.registerLayout({
|
|
52
|
+
filePath,
|
|
53
|
+
htmlLang: parsed.routeData.htmlLang,
|
|
54
|
+
title: parsed.routeData.title,
|
|
55
|
+
description: parsed.routeData.description,
|
|
56
|
+
canonical: parsed.routeData.canonical,
|
|
57
|
+
robots: parsed.routeData.robots,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const filePath of filePaths) {
|
|
62
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
63
|
+
if (!normalized.startsWith('src/pages/'))
|
|
64
|
+
continue;
|
|
65
|
+
if (!/\.(astro|md|mdx|html)$/i.test(normalized))
|
|
66
|
+
continue;
|
|
67
|
+
const relativeInsidePages = normalized.slice('src/pages/'.length);
|
|
68
|
+
const pattern = normalizeRoutePattern(relativeInsidePages, 'astro');
|
|
69
|
+
const inSitemap = sitemapPaths.has(pattern);
|
|
70
|
+
routes.push({
|
|
71
|
+
pattern,
|
|
72
|
+
filePath,
|
|
73
|
+
routerKind: 'astro',
|
|
74
|
+
renderingMode: 'static',
|
|
75
|
+
isPrivate: false,
|
|
76
|
+
inSitemap,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return routes;
|
|
80
|
+
}
|
|
81
|
+
async extractRoute(projectRoot, route, fileContent) {
|
|
82
|
+
const parsed = parseAstroOrMarkdown(route, fileContent);
|
|
83
|
+
let extracted = parsed.routeData;
|
|
84
|
+
// Apply layout inheritance: check layout candidates
|
|
85
|
+
const layoutCandidates = [
|
|
86
|
+
'src/layouts/BaseLayout.astro',
|
|
87
|
+
'src/layouts/Layout.astro',
|
|
88
|
+
'src/layouts/MainLayout.astro',
|
|
89
|
+
'src/layouts/DefaultLayout.astro',
|
|
90
|
+
];
|
|
91
|
+
extracted = this.layoutResolver.applyInheritance(extracted, layoutCandidates);
|
|
92
|
+
return extracted;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
export function patchAstroTemplate(sourceCode, meta) {
|
|
96
|
+
let result = sourceCode;
|
|
97
|
+
const entries = [];
|
|
98
|
+
if (meta.title)
|
|
99
|
+
entries.push(`title: "${meta.title.replace(/"/g, '\\"')}"`);
|
|
100
|
+
if (meta.description)
|
|
101
|
+
entries.push(`description: "${meta.description.replace(/"/g, '\\"')}"`);
|
|
102
|
+
if (meta.canonical)
|
|
103
|
+
entries.push(`canonical: "${meta.canonical}"`);
|
|
104
|
+
const seoVarBlock = `const seoMeta = {\n ${entries.join(',\n ')}\n};\n`;
|
|
105
|
+
const frontmatterMatch = /^---\s*\n([\s\S]*?)\n---/m.exec(result);
|
|
106
|
+
if (frontmatterMatch) {
|
|
107
|
+
result = result.replace(/^---\s*\n/m, `---\n${seoVarBlock}`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
result = `---\n${seoVarBlock}---\n\n` + result;
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=astro-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"astro-adapter.js","sourceRoot":"","sources":["../src/astro-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FrameworkType } from '@seo-partner/core';
|
|
2
|
+
export interface BlogScaffoldFile {
|
|
3
|
+
relativePath: string;
|
|
4
|
+
code: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateBlogScaffold(framework: FrameworkType, pillarTitle?: string): BlogScaffoldFile[];
|
|
8
|
+
//# sourceMappingURL=blog-scaffold-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-scaffold-generator.d.ts","sourceRoot":"","sources":["../src/blog-scaffold-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,WAAW,SAA6B,GACvC,gBAAgB,EAAE,CA4IpB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export function generateBlogScaffold(framework, pillarTitle = 'Actualités et Guides SEO') {
|
|
2
|
+
switch (framework) {
|
|
3
|
+
case 'next_app':
|
|
4
|
+
return [
|
|
5
|
+
{
|
|
6
|
+
relativePath: 'app/blog/page.tsx',
|
|
7
|
+
code: `import type { Metadata } from 'next';
|
|
8
|
+
import Link from 'next/link';
|
|
9
|
+
|
|
10
|
+
export const metadata: Metadata = {
|
|
11
|
+
title: '${pillarTitle} | Blog',
|
|
12
|
+
description: 'Découvrez nos guides d\\\'experts et analyses approfondies.',
|
|
13
|
+
alternates: {
|
|
14
|
+
canonical: '/blog',
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default function BlogIndexPage() {
|
|
19
|
+
return (
|
|
20
|
+
<main className="max-w-4xl mx-auto py-12 px-4">
|
|
21
|
+
<h1 className="text-3xl font-bold mb-8">${pillarTitle}</h1>
|
|
22
|
+
<p className="text-gray-600 mb-6">Explorez nos articles et guides complets pour approfondir vos compétences.</p>
|
|
23
|
+
</main>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
`,
|
|
27
|
+
description: 'Page d\'index du blog Next.js App Router',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
relativePath: 'app/blog/[slug]/page.tsx',
|
|
31
|
+
code: `import type { Metadata } from 'next';
|
|
32
|
+
import { notFound } from 'next/navigation';
|
|
33
|
+
|
|
34
|
+
interface Props {
|
|
35
|
+
params: { slug: string };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|
39
|
+
return {
|
|
40
|
+
title: \`Article: \${params.slug} | Blog\`,
|
|
41
|
+
description: 'Guide complet et recommandations pratiques.',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function BlogPostPage({ params }: Props) {
|
|
46
|
+
return (
|
|
47
|
+
<article className="max-w-3xl mx-auto py-12 px-4 prose">
|
|
48
|
+
<h1>Détail de l\\\'article : {params.slug}</h1>
|
|
49
|
+
</article>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
`,
|
|
53
|
+
description: 'Route dynamique d\'article Next.js App Router',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
case 'nuxt':
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
relativePath: 'pages/blog/index.vue',
|
|
60
|
+
code: `<script setup lang="ts">
|
|
61
|
+
useSeoMeta({
|
|
62
|
+
title: '${pillarTitle} | Blog',
|
|
63
|
+
description: 'Guides d\\\'experts et ressources pratiques.',
|
|
64
|
+
});
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<main class="blog-index">
|
|
69
|
+
<h1>${pillarTitle}</h1>
|
|
70
|
+
</main>
|
|
71
|
+
</template>
|
|
72
|
+
`,
|
|
73
|
+
description: 'Page d\'index du blog Nuxt 3',
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
case 'astro':
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
relativePath: 'src/pages/blog/index.astro',
|
|
80
|
+
code: `---
|
|
81
|
+
const pageTitle = "${pillarTitle}";
|
|
82
|
+
---
|
|
83
|
+
<html>
|
|
84
|
+
<head>
|
|
85
|
+
<title>{pageTitle}</title>
|
|
86
|
+
<meta name="description" content="Guides et analyses éditoriales d\\\'experts." />
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
<main>
|
|
90
|
+
<h1>{pageTitle}</h1>
|
|
91
|
+
</main>
|
|
92
|
+
</body>
|
|
93
|
+
</html>
|
|
94
|
+
`,
|
|
95
|
+
description: 'Page d\'index du blog Astro',
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
case 'laravel':
|
|
99
|
+
return [
|
|
100
|
+
{
|
|
101
|
+
relativePath: 'resources/views/blog/index.blade.php',
|
|
102
|
+
code: `@extends('layouts.app')
|
|
103
|
+
|
|
104
|
+
@section('title', '${pillarTitle}')
|
|
105
|
+
|
|
106
|
+
@push('meta')
|
|
107
|
+
<meta name="description" content="Retrouvez nos guides et articles spécialisés.">
|
|
108
|
+
@endpush
|
|
109
|
+
|
|
110
|
+
@section('content')
|
|
111
|
+
<div class="container py-5">
|
|
112
|
+
<h1>${pillarTitle}</h1>
|
|
113
|
+
</div>
|
|
114
|
+
@endsection
|
|
115
|
+
`,
|
|
116
|
+
description: 'Vue d\'index du blog Laravel Blade',
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
default:
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
relativePath: 'blog/index.php',
|
|
123
|
+
code: `<?php
|
|
124
|
+
$pageTitle = '${pillarTitle}';
|
|
125
|
+
$metaDescription = 'Guides et ressources d\\\'experts.';
|
|
126
|
+
require_once __DIR__ . '/../includes/header.php';
|
|
127
|
+
?>
|
|
128
|
+
<main class="container">
|
|
129
|
+
<h1><?php echo htmlspecialchars($pageTitle); ?></h1>
|
|
130
|
+
</main>
|
|
131
|
+
<?php require_once __DIR__ . '/../includes/footer.php'; ?>
|
|
132
|
+
`,
|
|
133
|
+
description: 'Script d\'index du blog PHP',
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=blog-scaffold-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blog-scaffold-generator.js","sourceRoot":"","sources":["../src/blog-scaffold-generator.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,oBAAoB,CAClC,SAAwB,EACxB,WAAW,GAAG,0BAA0B;IAExC,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,UAAU;YACb,OAAO;gBACL;oBACE,YAAY,EAAE,mBAAmB;oBACjC,IAAI,EAAE;;;;YAIJ,WAAW;;;;;;;;;;gDAUyB,WAAW;;;;;CAK1D;oBACS,WAAW,EAAE,0CAA0C;iBACxD;gBACD;oBACE,YAAY,EAAE,0BAA0B;oBACxC,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;CAqBf;oBACS,WAAW,EAAE,+CAA+C;iBAC7D;aACF,CAAC;QAEJ,KAAK,MAAM;YACT,OAAO;gBACL;oBACE,YAAY,EAAE,sBAAsB;oBACpC,IAAI,EAAE;;YAEJ,WAAW;;;;;;;UAOb,WAAW;;;CAGpB;oBACS,WAAW,EAAE,8BAA8B;iBAC5C;aACF,CAAC;QAEJ,KAAK,OAAO;YACV,OAAO;gBACL;oBACE,YAAY,EAAE,4BAA4B;oBAC1C,IAAI,EAAE;qBACK,WAAW;;;;;;;;;;;;;CAa/B;oBACS,WAAW,EAAE,6BAA6B;iBAC3C;aACF,CAAC;QAEJ,KAAK,SAAS;YACZ,OAAO;gBACL;oBACE,YAAY,EAAE,sCAAsC;oBACpD,IAAI,EAAE;;qBAEK,WAAW;;;;;;;;UAQtB,WAAW;;;CAGpB;oBACS,WAAW,EAAE,oCAAoC;iBAClD;aACF,CAAC;QAEJ;YACE,OAAO;gBACL;oBACE,YAAY,EAAE,gBAAgB;oBAC9B,IAAI,EAAE;gBACA,WAAW;;;;;;;;CAQ1B;oBACS,WAAW,EAAE,6BAA6B;iBAC3C;aACF,CAAC;IACN,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DiscoveredRoute, ExtractedRouteData } from '@seo-partner/core';
|
|
2
|
+
export type AdapterVersion = '1.0';
|
|
3
|
+
export interface AdapterCapabilities {
|
|
4
|
+
readonly routes: boolean;
|
|
5
|
+
readonly metadata: boolean;
|
|
6
|
+
readonly headings: boolean;
|
|
7
|
+
readonly internalLinks: boolean;
|
|
8
|
+
readonly jsonLd: boolean;
|
|
9
|
+
readonly images: boolean;
|
|
10
|
+
readonly robotsAndSitemap: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface FrameworkAdapter {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly version: AdapterVersion;
|
|
16
|
+
readonly capabilities: AdapterCapabilities;
|
|
17
|
+
detect(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): Promise<boolean>;
|
|
18
|
+
discoverRoutes(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>, sitemapPaths: ReadonlySet<string>): Promise<ReadonlyArray<DiscoveredRoute>>;
|
|
19
|
+
extractRoute(projectRoot: string, route: DiscoveredRoute, fileContent: string): Promise<ExtractedRouteData>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,MAAM,MAAM,cAAc,GAAG,KAAK,CAAC;AAEnC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAE3C,MAAM,CACJ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,cAAc,CACZ,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC;IAE3C,YAAY,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAChC"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FrameworkAdapter } from './contract.js';
|
|
2
|
+
/**
|
|
3
|
+
* Detection order contract (strictly prioritized to avoid ambiguities):
|
|
4
|
+
* 1. Next.js
|
|
5
|
+
* 2. Astro
|
|
6
|
+
* 3. Nuxt 3
|
|
7
|
+
* 4. SvelteKit
|
|
8
|
+
* 5. Remix / React Router v7
|
|
9
|
+
* 6. PHP (Laravel / Symfony / Native)
|
|
10
|
+
* 7. Django (Python)
|
|
11
|
+
* 8. Static HTML
|
|
12
|
+
*/
|
|
13
|
+
export declare const BUILTIN_ADAPTERS: ReadonlyArray<FrameworkAdapter>;
|
|
14
|
+
export declare function detectActiveAdapters(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): Promise<ReadonlyArray<FrameworkAdapter>>;
|
|
15
|
+
//# sourceMappingURL=detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAUtD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAAa,CAAC,gBAAgB,CAS5D,CAAC;AAEF,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAS1C"}
|
package/dist/detector.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { NextJsAdapter } from './nextjs-adapter.js';
|
|
2
|
+
import { AstroAdapter } from './astro-adapter.js';
|
|
3
|
+
import { NuxtAdapter } from './nuxt-adapter.js';
|
|
4
|
+
import { SvelteKitAdapter } from './sveltekit-adapter.js';
|
|
5
|
+
import { RemixAdapter } from './remix-adapter.js';
|
|
6
|
+
import { PhpAdapter } from './php-adapter.js';
|
|
7
|
+
import { DjangoAdapter } from './django-adapter.js';
|
|
8
|
+
import { StaticAdapter } from './static-adapter.js';
|
|
9
|
+
/**
|
|
10
|
+
* Detection order contract (strictly prioritized to avoid ambiguities):
|
|
11
|
+
* 1. Next.js
|
|
12
|
+
* 2. Astro
|
|
13
|
+
* 3. Nuxt 3
|
|
14
|
+
* 4. SvelteKit
|
|
15
|
+
* 5. Remix / React Router v7
|
|
16
|
+
* 6. PHP (Laravel / Symfony / Native)
|
|
17
|
+
* 7. Django (Python)
|
|
18
|
+
* 8. Static HTML
|
|
19
|
+
*/
|
|
20
|
+
export const BUILTIN_ADAPTERS = [
|
|
21
|
+
new NextJsAdapter(),
|
|
22
|
+
new AstroAdapter(),
|
|
23
|
+
new NuxtAdapter(),
|
|
24
|
+
new SvelteKitAdapter(),
|
|
25
|
+
new RemixAdapter(),
|
|
26
|
+
new PhpAdapter(),
|
|
27
|
+
new DjangoAdapter(),
|
|
28
|
+
new StaticAdapter(),
|
|
29
|
+
];
|
|
30
|
+
export async function detectActiveAdapters(projectRoot, filePaths, contentsByPath) {
|
|
31
|
+
const active = [];
|
|
32
|
+
for (const adapter of BUILTIN_ADAPTERS) {
|
|
33
|
+
const matched = await adapter.detect(projectRoot, filePaths, contentsByPath);
|
|
34
|
+
if (matched) {
|
|
35
|
+
active.push(adapter);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return active;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detector.js","sourceRoot":"","sources":["../src/detector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC;IAC/D,IAAI,aAAa,EAAE;IACnB,IAAI,YAAY,EAAE;IAClB,IAAI,WAAW,EAAE;IACjB,IAAI,gBAAgB,EAAE;IACtB,IAAI,YAAY,EAAE;IAClB,IAAI,UAAU,EAAE;IAChB,IAAI,aAAa,EAAE;IACnB,IAAI,aAAa,EAAE;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,SAAgC,EAChC,cAA2C;IAE3C,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;QAC7E,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DiscoveredRoute, ExtractedRouteData } from '@seo-partner/core';
|
|
2
|
+
import type { AdapterCapabilities, AdapterVersion, FrameworkAdapter } from './contract.js';
|
|
3
|
+
export declare class DjangoAdapter implements FrameworkAdapter {
|
|
4
|
+
readonly id = "django";
|
|
5
|
+
readonly name = "Django";
|
|
6
|
+
readonly version: AdapterVersion;
|
|
7
|
+
readonly capabilities: AdapterCapabilities;
|
|
8
|
+
private readonly layoutResolver;
|
|
9
|
+
detect(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>): Promise<boolean>;
|
|
10
|
+
discoverRoutes(projectRoot: string, filePaths: ReadonlyArray<string>, contentsByPath: ReadonlyMap<string, string>, sitemapPaths: ReadonlySet<string>): Promise<ReadonlyArray<DiscoveredRoute>>;
|
|
11
|
+
extractRoute(projectRoot: string, route: DiscoveredRoute, fileContent: string): Promise<ExtractedRouteData>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=django-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"django-adapter.d.ts","sourceRoot":"","sources":["../src/django-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAK3F,qBAAa,aAAc,YAAW,gBAAgB;IACpD,SAAgB,EAAE,YAAY;IAC9B,SAAgB,IAAI,YAAY;IAChC,SAAgB,OAAO,EAAE,cAAc,CAAS;IAChD,SAAgB,YAAY,EAAE,mBAAmB,CAQ/C;IAEF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IAE1C,MAAM,CACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAC1C,OAAO,CAAC,OAAO,CAAC;IAkBN,cAAc,CACzB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAoG7B,YAAY,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,eAAe,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC;CAiB/B"}
|