@djangocfg/seo 2.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +3780 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/crawler/index.d.ts +88 -0
- package/dist/crawler/index.mjs +610 -0
- package/dist/crawler/index.mjs.map +1 -0
- package/dist/google-console/index.d.ts +95 -0
- package/dist/google-console/index.mjs +539 -0
- package/dist/google-console/index.mjs.map +1 -0
- package/dist/index.d.ts +285 -0
- package/dist/index.mjs +3236 -0
- package/dist/index.mjs.map +1 -0
- package/dist/link-checker/index.d.ts +76 -0
- package/dist/link-checker/index.mjs +326 -0
- package/dist/link-checker/index.mjs.map +1 -0
- package/dist/markdown-report-B3QdDzxE.d.ts +193 -0
- package/dist/reports/index.d.ts +24 -0
- package/dist/reports/index.mjs +836 -0
- package/dist/reports/index.mjs.map +1 -0
- package/dist/routes/index.d.ts +69 -0
- package/dist/routes/index.mjs +372 -0
- package/dist/routes/index.mjs.map +1 -0
- package/dist/scanner-Cz4Th2Pt.d.ts +60 -0
- package/dist/types/index.d.ts +144 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +114 -0
- package/src/analyzer.ts +256 -0
- package/src/cli/commands/audit.ts +260 -0
- package/src/cli/commands/content.ts +180 -0
- package/src/cli/commands/crawl.ts +32 -0
- package/src/cli/commands/index.ts +12 -0
- package/src/cli/commands/inspect.ts +60 -0
- package/src/cli/commands/links.ts +41 -0
- package/src/cli/commands/robots.ts +36 -0
- package/src/cli/commands/routes.ts +126 -0
- package/src/cli/commands/sitemap.ts +48 -0
- package/src/cli/index.ts +149 -0
- package/src/cli/types.ts +40 -0
- package/src/config.ts +207 -0
- package/src/content/index.ts +51 -0
- package/src/content/link-checker.ts +182 -0
- package/src/content/link-fixer.ts +188 -0
- package/src/content/scanner.ts +200 -0
- package/src/content/sitemap-generator.ts +321 -0
- package/src/content/types.ts +140 -0
- package/src/crawler/crawler.ts +425 -0
- package/src/crawler/index.ts +10 -0
- package/src/crawler/robots-parser.ts +171 -0
- package/src/crawler/sitemap-validator.ts +204 -0
- package/src/google-console/analyzer.ts +317 -0
- package/src/google-console/auth.ts +100 -0
- package/src/google-console/client.ts +281 -0
- package/src/google-console/index.ts +9 -0
- package/src/index.ts +144 -0
- package/src/link-checker/index.ts +461 -0
- package/src/reports/claude-context.ts +149 -0
- package/src/reports/generator.ts +244 -0
- package/src/reports/index.ts +27 -0
- package/src/reports/json-report.ts +320 -0
- package/src/reports/markdown-report.ts +246 -0
- package/src/reports/split-report.ts +252 -0
- package/src/routes/analyzer.ts +324 -0
- package/src/routes/index.ts +25 -0
- package/src/routes/scanner.ts +298 -0
- package/src/types/index.ts +222 -0
- package/src/utils/index.ts +154 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Sitemap Generator for Next.js App Router projects
|
|
3
|
+
* Supports both Nextra (MDX content) and standard Next.js projects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import type { SitemapItem, SitemapData, ContentConfig } from './types.js';
|
|
9
|
+
import { scanRoutes, type RouteInfo } from '../routes/scanner.js';
|
|
10
|
+
import { detectProjectType } from './scanner.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Dynamically import _meta.ts file (Nextra pattern)
|
|
14
|
+
*/
|
|
15
|
+
async function getMeta(dir: string): Promise<Record<string, unknown>> {
|
|
16
|
+
const metaPath = path.join(dir, '_meta.ts');
|
|
17
|
+
if (!fs.existsSync(metaPath)) return {};
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const meta = await import(metaPath);
|
|
21
|
+
return meta.default || {};
|
|
22
|
+
} catch {
|
|
23
|
+
// Fallback: try to parse with regex for simple cases
|
|
24
|
+
try {
|
|
25
|
+
const content = fs.readFileSync(metaPath, 'utf-8');
|
|
26
|
+
const matches = content.matchAll(/'([^']+)':\s*['"]([^'"]+)['"]/g);
|
|
27
|
+
const result: Record<string, string> = {};
|
|
28
|
+
for (const match of matches) {
|
|
29
|
+
const key = match[1];
|
|
30
|
+
const value = match[2];
|
|
31
|
+
if (key && value) {
|
|
32
|
+
result[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
} catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get title from meta object
|
|
44
|
+
*/
|
|
45
|
+
function getTitleFromMeta(key: string, meta: Record<string, unknown>): string {
|
|
46
|
+
const value = meta[key];
|
|
47
|
+
if (!value) return key;
|
|
48
|
+
if (typeof value === 'string') return value;
|
|
49
|
+
if (typeof value === 'object' && value !== null && 'title' in value) {
|
|
50
|
+
return String((value as { title: unknown }).title);
|
|
51
|
+
}
|
|
52
|
+
return key;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Scan Nextra content directory recursively
|
|
57
|
+
*/
|
|
58
|
+
async function scanContent(
|
|
59
|
+
dir: string,
|
|
60
|
+
baseUrl: string = '/docs'
|
|
61
|
+
): Promise<SitemapItem[]> {
|
|
62
|
+
const items: SitemapItem[] = [];
|
|
63
|
+
|
|
64
|
+
if (!fs.existsSync(dir)) return items;
|
|
65
|
+
|
|
66
|
+
const meta = await getMeta(dir);
|
|
67
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
68
|
+
|
|
69
|
+
// Build file map
|
|
70
|
+
const fileMap = new Map<string, fs.Dirent>();
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const key = entry.name.replace(/\.mdx?$/, '');
|
|
73
|
+
fileMap.set(key, entry);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Process meta keys first to preserve order
|
|
77
|
+
const metaKeys = Object.keys(meta);
|
|
78
|
+
for (const key of metaKeys) {
|
|
79
|
+
const entry = fileMap.get(key);
|
|
80
|
+
if (!entry) continue;
|
|
81
|
+
|
|
82
|
+
fileMap.delete(key);
|
|
83
|
+
const fullPath = path.join(dir, entry.name);
|
|
84
|
+
const itemPath = path.join(baseUrl, key).replace(/\\/g, '/');
|
|
85
|
+
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
const children = await scanContent(fullPath, itemPath);
|
|
88
|
+
items.push({
|
|
89
|
+
title: getTitleFromMeta(key, meta),
|
|
90
|
+
path: itemPath,
|
|
91
|
+
children: children.length > 0 ? children : undefined,
|
|
92
|
+
});
|
|
93
|
+
} else if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) {
|
|
94
|
+
if (entry.name !== 'index.mdx' && entry.name !== 'index.md') {
|
|
95
|
+
items.push({
|
|
96
|
+
title: getTitleFromMeta(key, meta),
|
|
97
|
+
path: itemPath,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Process remaining entries not in meta
|
|
104
|
+
for (const [key, entry] of fileMap.entries()) {
|
|
105
|
+
if (key.startsWith('_') || key.startsWith('.')) continue;
|
|
106
|
+
|
|
107
|
+
const fullPath = path.join(dir, entry.name);
|
|
108
|
+
const itemPath = path.join(baseUrl, key).replace(/\\/g, '/');
|
|
109
|
+
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
const children = await scanContent(fullPath, itemPath);
|
|
112
|
+
items.push({
|
|
113
|
+
title: key,
|
|
114
|
+
path: itemPath,
|
|
115
|
+
children: children.length > 0 ? children : undefined,
|
|
116
|
+
});
|
|
117
|
+
} else if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mdx'))) {
|
|
118
|
+
if (entry.name !== 'index.mdx' && entry.name !== 'index.md') {
|
|
119
|
+
items.push({
|
|
120
|
+
title: key,
|
|
121
|
+
path: itemPath,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return items;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Convert route path to title
|
|
132
|
+
*/
|
|
133
|
+
function routeToTitle(routePath: string): string {
|
|
134
|
+
const segment = routePath.split('/').filter(Boolean).pop() || 'Home';
|
|
135
|
+
// Skip dynamic segments
|
|
136
|
+
if (segment.startsWith('[')) return segment;
|
|
137
|
+
// Capitalize and replace hyphens
|
|
138
|
+
return segment
|
|
139
|
+
.split('-')
|
|
140
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
141
|
+
.join(' ');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert routes to sitemap items (tree structure)
|
|
146
|
+
* Uses existing routes/scanner.ts for universal Next.js support
|
|
147
|
+
*/
|
|
148
|
+
function routesToSitemapItems(routes: RouteInfo[]): SitemapItem[] {
|
|
149
|
+
// Build tree from flat routes
|
|
150
|
+
const root: Map<string, SitemapItem> = new Map();
|
|
151
|
+
const items: SitemapItem[] = [];
|
|
152
|
+
|
|
153
|
+
// Sort routes by path depth
|
|
154
|
+
const sortedRoutes = [...routes]
|
|
155
|
+
.filter(r => r.type === 'page' && !r.isDynamic) // Only static pages
|
|
156
|
+
.sort((a, b) => a.path.split('/').length - b.path.split('/').length);
|
|
157
|
+
|
|
158
|
+
for (const route of sortedRoutes) {
|
|
159
|
+
const segments = route.path.split('/').filter(Boolean);
|
|
160
|
+
|
|
161
|
+
if (segments.length === 0) {
|
|
162
|
+
// Root path
|
|
163
|
+
items.push({
|
|
164
|
+
title: 'Home',
|
|
165
|
+
path: '/',
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Find or create parent
|
|
171
|
+
let currentPath = '';
|
|
172
|
+
let parentChildren: SitemapItem[] = items;
|
|
173
|
+
|
|
174
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
175
|
+
currentPath += '/' + segments[i];
|
|
176
|
+
let parent = root.get(currentPath);
|
|
177
|
+
|
|
178
|
+
if (!parent) {
|
|
179
|
+
parent = {
|
|
180
|
+
title: routeToTitle(currentPath),
|
|
181
|
+
path: currentPath,
|
|
182
|
+
children: [],
|
|
183
|
+
};
|
|
184
|
+
root.set(currentPath, parent);
|
|
185
|
+
parentChildren.push(parent);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
parentChildren = parent.children || (parent.children = []);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Add current item
|
|
192
|
+
const item: SitemapItem = {
|
|
193
|
+
title: routeToTitle(route.path),
|
|
194
|
+
path: route.path,
|
|
195
|
+
};
|
|
196
|
+
parentChildren.push(item);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return items;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Generate sitemap data from project
|
|
204
|
+
* Auto-detects project type (Nextra vs standard Next.js)
|
|
205
|
+
*/
|
|
206
|
+
export async function generateSitemapData(
|
|
207
|
+
cwd: string,
|
|
208
|
+
config?: Partial<ContentConfig>
|
|
209
|
+
): Promise<SitemapData> {
|
|
210
|
+
const projectType = detectProjectType(cwd);
|
|
211
|
+
const contentDir = path.join(cwd, config?.contentDir || 'content');
|
|
212
|
+
const appDir = path.join(cwd, config?.appDir || 'app');
|
|
213
|
+
const basePath = config?.basePath || '/docs';
|
|
214
|
+
|
|
215
|
+
let docsItems: SitemapItem[] = [];
|
|
216
|
+
let appItems: SitemapItem[] = [];
|
|
217
|
+
|
|
218
|
+
// Scan content/ for Nextra projects
|
|
219
|
+
if (projectType === 'nextra' && fs.existsSync(contentDir)) {
|
|
220
|
+
docsItems = await scanContent(contentDir, basePath);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Scan app/ using routes scanner (universal)
|
|
224
|
+
if (fs.existsSync(appDir)) {
|
|
225
|
+
try {
|
|
226
|
+
const scanResult = scanRoutes({ appDir, includeApi: false });
|
|
227
|
+
appItems = routesToSitemapItems(scanResult.routes);
|
|
228
|
+
} catch {
|
|
229
|
+
// Fallback to simple scan if routes scanner fails
|
|
230
|
+
appItems = [];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
app: appItems,
|
|
236
|
+
docs: docsItems,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate TypeScript file content
|
|
242
|
+
*/
|
|
243
|
+
function generateTsContent(data: SitemapData): string {
|
|
244
|
+
return `
|
|
245
|
+
// This file is auto-generated by @djangocfg/seo
|
|
246
|
+
// Do not edit manually
|
|
247
|
+
|
|
248
|
+
export interface SitemapItem {
|
|
249
|
+
title: string;
|
|
250
|
+
path: string;
|
|
251
|
+
children?: SitemapItem[];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const sitemap: { app: SitemapItem[], docs: SitemapItem[] } = ${JSON.stringify(data, null, 2)};
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Generate and save sitemap file
|
|
260
|
+
*/
|
|
261
|
+
export async function generateSitemap(
|
|
262
|
+
cwd: string,
|
|
263
|
+
options: {
|
|
264
|
+
output?: string;
|
|
265
|
+
config?: Partial<ContentConfig>;
|
|
266
|
+
} = {}
|
|
267
|
+
): Promise<{ outputPath: string; data: SitemapData }> {
|
|
268
|
+
const output = options.output || 'app/_core/sitemap.ts';
|
|
269
|
+
const outputPath = path.join(cwd, output);
|
|
270
|
+
|
|
271
|
+
// Generate data
|
|
272
|
+
const data = await generateSitemapData(cwd, options.config);
|
|
273
|
+
|
|
274
|
+
// Ensure output directory exists
|
|
275
|
+
const outputDir = path.dirname(outputPath);
|
|
276
|
+
if (!fs.existsSync(outputDir)) {
|
|
277
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Write file
|
|
281
|
+
const content = generateTsContent(data);
|
|
282
|
+
fs.writeFileSync(outputPath, content);
|
|
283
|
+
|
|
284
|
+
return { outputPath, data };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Flatten sitemap items to list of paths
|
|
289
|
+
*/
|
|
290
|
+
export function flattenSitemap(items: SitemapItem[]): string[] {
|
|
291
|
+
const paths: string[] = [];
|
|
292
|
+
|
|
293
|
+
function traverse(item: SitemapItem) {
|
|
294
|
+
paths.push(item.path);
|
|
295
|
+
if (item.children) {
|
|
296
|
+
for (const child of item.children) {
|
|
297
|
+
traverse(child);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
for (const item of items) {
|
|
303
|
+
traverse(item);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return paths;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Count total items in sitemap
|
|
311
|
+
*/
|
|
312
|
+
export function countSitemapItems(data: SitemapData): { app: number; docs: number; total: number } {
|
|
313
|
+
const appPaths = flattenSitemap(data.app);
|
|
314
|
+
const docsPaths = flattenSitemap(data.docs);
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
app: appPaths.length,
|
|
318
|
+
docs: docsPaths.length,
|
|
319
|
+
total: appPaths.length + docsPaths.length,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content module types for MDX/Nextra projects
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ContentConfig {
|
|
6
|
+
/** Content directory path (default: 'content') */
|
|
7
|
+
contentDir: string;
|
|
8
|
+
/** App directory path (default: 'app') */
|
|
9
|
+
appDir: string;
|
|
10
|
+
/** Base URL path for docs (default: '/docs') */
|
|
11
|
+
basePath: string;
|
|
12
|
+
/** File extensions to scan (default: ['.mdx', '.md']) */
|
|
13
|
+
extensions: string[];
|
|
14
|
+
/** Asset extensions to ignore in links */
|
|
15
|
+
assetExtensions: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SitemapConfig {
|
|
19
|
+
/** Output file path (default: 'app/_core/sitemap.ts') */
|
|
20
|
+
output: string;
|
|
21
|
+
/** Include app pages in sitemap */
|
|
22
|
+
includeApp: boolean;
|
|
23
|
+
/** Include content pages in sitemap */
|
|
24
|
+
includeContent: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SitemapItem {
|
|
28
|
+
title: string;
|
|
29
|
+
path: string;
|
|
30
|
+
children?: SitemapItem[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SitemapData {
|
|
34
|
+
app: SitemapItem[];
|
|
35
|
+
docs: SitemapItem[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface FileInfo {
|
|
39
|
+
/** Full path to the file */
|
|
40
|
+
fullPath: string;
|
|
41
|
+
/** Path relative to content directory */
|
|
42
|
+
relativePath: string;
|
|
43
|
+
/** Whether this is an index file */
|
|
44
|
+
isIndex: boolean;
|
|
45
|
+
/** Parent folder path */
|
|
46
|
+
folder: string;
|
|
47
|
+
/** File name without extension */
|
|
48
|
+
name: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type LinkType = 'absolute' | 'dotslash' | 'parent' | 'simple';
|
|
52
|
+
|
|
53
|
+
export interface ExtractedLink {
|
|
54
|
+
/** Raw link as found in file */
|
|
55
|
+
raw: string;
|
|
56
|
+
/** Resolved absolute path */
|
|
57
|
+
resolved: string;
|
|
58
|
+
/** Type of link syntax */
|
|
59
|
+
type: LinkType;
|
|
60
|
+
/** Line number in file */
|
|
61
|
+
line: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface BrokenLink {
|
|
65
|
+
/** File containing the broken link */
|
|
66
|
+
file: string;
|
|
67
|
+
/** Full link path (e.g., /docs/path) */
|
|
68
|
+
link: string;
|
|
69
|
+
/** Link type */
|
|
70
|
+
type: LinkType;
|
|
71
|
+
/** Raw link text */
|
|
72
|
+
raw: string;
|
|
73
|
+
/** Line number */
|
|
74
|
+
line: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface LinkCheckResult {
|
|
78
|
+
/** Total files checked */
|
|
79
|
+
filesChecked: number;
|
|
80
|
+
/** Unique links checked */
|
|
81
|
+
uniqueLinks: number;
|
|
82
|
+
/** Broken links found */
|
|
83
|
+
brokenLinks: BrokenLink[];
|
|
84
|
+
/** All links are valid */
|
|
85
|
+
success: boolean;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface LinkFix {
|
|
89
|
+
/** Original link */
|
|
90
|
+
from: string;
|
|
91
|
+
/** Replacement link */
|
|
92
|
+
to: string;
|
|
93
|
+
/** Line number */
|
|
94
|
+
line: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface LinkFixResult {
|
|
98
|
+
/** File path */
|
|
99
|
+
file: string;
|
|
100
|
+
/** Full path to file */
|
|
101
|
+
fullPath: string;
|
|
102
|
+
/** Fixes applied/suggested */
|
|
103
|
+
fixes: LinkFix[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface FixLinksResult {
|
|
107
|
+
/** Total changes made/suggested */
|
|
108
|
+
totalChanges: number;
|
|
109
|
+
/** Files with changes */
|
|
110
|
+
fileChanges: LinkFixResult[];
|
|
111
|
+
/** Whether fixes were applied */
|
|
112
|
+
applied: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface ContentScanResult {
|
|
116
|
+
/** Project type detected */
|
|
117
|
+
projectType: 'nextra' | 'nextjs' | 'unknown';
|
|
118
|
+
/** Content directory found */
|
|
119
|
+
hasContent: boolean;
|
|
120
|
+
/** App directory found */
|
|
121
|
+
hasApp: boolean;
|
|
122
|
+
/** MDX files found */
|
|
123
|
+
mdxFiles: string[];
|
|
124
|
+
/** Page files found in app/ */
|
|
125
|
+
pageFiles: string[];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const DEFAULT_CONFIG: ContentConfig = {
|
|
129
|
+
contentDir: 'content',
|
|
130
|
+
appDir: 'app',
|
|
131
|
+
basePath: '/docs',
|
|
132
|
+
extensions: ['.mdx', '.md'],
|
|
133
|
+
assetExtensions: ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.pdf', '.zip', '.tar', '.gz'],
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const DEFAULT_SITEMAP_CONFIG: SitemapConfig = {
|
|
137
|
+
output: 'app/_core/sitemap.ts',
|
|
138
|
+
includeApp: true,
|
|
139
|
+
includeContent: true,
|
|
140
|
+
};
|