@duffcloudservices/cms 0.6.0 → 0.8.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/dist/chunk-KCWMS7P4.js +3 -0
- package/dist/chunk-KCWMS7P4.js.map +1 -0
- package/dist/chunk-UPAMLKOQ.js +1022 -0
- package/dist/chunk-UPAMLKOQ.js.map +1 -0
- package/dist/index.d.ts +4 -195
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/index.d.ts +72 -28
- package/dist/plugins/index.js +224 -10
- package/dist/plugins/index.js.map +1 -1
- package/dist/seo/index.d.ts +248 -0
- package/dist/seo/index.js +4 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/vitepressTransform-DeEzgGWU.d.ts +979 -0
- package/package.json +7 -3
- package/dist/chunk-JJK7OGC2.js +0 -459
- package/dist/chunk-JJK7OGC2.js.map +0 -1
- package/dist/vitepressTransform-D9P9D7So.d.ts +0 -479
package/dist/plugins/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { buildHeadTags, spliceHeadHtml, loadPagesManifest } from '../chunk-
|
|
2
|
-
export { buildVitePressSeoHead, createSeoTransformPageData, defaultRelativePathToRoute } from '../chunk-
|
|
1
|
+
import { buildSitemapXml, isHandAuthoredRobotsAcceptable, buildRobotsTxt, buildLlmsTxt, matchesExcludedGlob, breadcrumbTrailFromRoute, findReviewItemsForPage, buildHeadTags, spliceHeadHtml, loadPagesManifest } from '../chunk-UPAMLKOQ.js';
|
|
2
|
+
export { buildVitePressSeoHead, createSeoTransformPageData, defaultRelativePathToRoute } from '../chunk-UPAMLKOQ.js';
|
|
3
3
|
import fs2 from 'fs';
|
|
4
4
|
import path2 from 'path';
|
|
5
|
-
import
|
|
5
|
+
import yaml2 from 'js-yaml';
|
|
6
6
|
import { defineComponent, h } from 'vue';
|
|
7
7
|
|
|
8
8
|
function dcsContentPlugin(options = {}) {
|
|
@@ -41,7 +41,7 @@ function dcsContentPlugin(options = {}) {
|
|
|
41
41
|
}
|
|
42
42
|
try {
|
|
43
43
|
const fileContent = fs2.readFileSync(foundPath, "utf8");
|
|
44
|
-
const content =
|
|
44
|
+
const content = yaml2.load(fileContent);
|
|
45
45
|
if (debug) {
|
|
46
46
|
console.log(`[dcs-content] Loaded ${foundPath}`);
|
|
47
47
|
console.log(`[dcs-content] Version: ${content.version}`);
|
|
@@ -112,33 +112,207 @@ function loadSeoConfig(projectRoot, seoPath, debug) {
|
|
|
112
112
|
}
|
|
113
113
|
try {
|
|
114
114
|
const fileContent = fs2.readFileSync(foundPath, "utf8");
|
|
115
|
-
const config =
|
|
115
|
+
const config = yaml2.load(fileContent);
|
|
116
116
|
return { config, foundPath };
|
|
117
117
|
} catch (error) {
|
|
118
118
|
console.warn("[dcs-seo] Failed to parse seo.yaml:", error);
|
|
119
119
|
return null;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
function loadContentConfig(projectRoot, contentRelPath, debug) {
|
|
123
|
+
const possiblePaths = [
|
|
124
|
+
path2.resolve(projectRoot, contentRelPath),
|
|
125
|
+
path2.resolve(projectRoot, "..", contentRelPath),
|
|
126
|
+
path2.resolve(process.cwd(), contentRelPath)
|
|
127
|
+
];
|
|
128
|
+
const foundPath = possiblePaths.find((p) => fs2.existsSync(p));
|
|
129
|
+
if (!foundPath) {
|
|
130
|
+
if (debug) console.log(`[dcs-seo] No content.yaml found (reviews disabled)`);
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const parsed = yaml2.load(fs2.readFileSync(foundPath, "utf8"));
|
|
135
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
136
|
+
return parsed;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.warn("[dcs-seo] Failed to parse content.yaml (reviews disabled):", error);
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
122
142
|
function routeToOutputFile(outDir, routePath) {
|
|
123
143
|
const trimmed = routePath.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
124
144
|
if (trimmed === "") return path2.join(outDir, "index.html");
|
|
125
145
|
return path2.join(outDir, ...trimmed.split("/"), "index.html");
|
|
126
146
|
}
|
|
147
|
+
function loadExcludedGlobs(projectRoot, pagesPath) {
|
|
148
|
+
const possiblePaths = [
|
|
149
|
+
path2.resolve(projectRoot, pagesPath),
|
|
150
|
+
path2.resolve(projectRoot, "..", pagesPath),
|
|
151
|
+
path2.resolve(process.cwd(), pagesPath)
|
|
152
|
+
];
|
|
153
|
+
const found = possiblePaths.find((p) => fs2.existsSync(p));
|
|
154
|
+
if (!found) return [];
|
|
155
|
+
try {
|
|
156
|
+
const raw = yaml2.load(fs2.readFileSync(found, "utf8"));
|
|
157
|
+
const excluded = raw?.excluded;
|
|
158
|
+
if (!Array.isArray(excluded)) return [];
|
|
159
|
+
return excluded.filter((e) => typeof e === "string");
|
|
160
|
+
} catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function deriveLastmod(seoConfig, projectRoot, pagesPath) {
|
|
165
|
+
const fromSeo = seoConfig?.lastUpdated;
|
|
166
|
+
if (typeof fromSeo === "string" && fromSeo.length > 0) return fromSeo;
|
|
167
|
+
const possiblePaths = [
|
|
168
|
+
path2.resolve(projectRoot, pagesPath),
|
|
169
|
+
path2.resolve(projectRoot, "..", pagesPath),
|
|
170
|
+
path2.resolve(process.cwd(), pagesPath)
|
|
171
|
+
];
|
|
172
|
+
const found = possiblePaths.find((p) => fs2.existsSync(p));
|
|
173
|
+
if (!found) return void 0;
|
|
174
|
+
try {
|
|
175
|
+
const raw = yaml2.load(fs2.readFileSync(found, "utf8"));
|
|
176
|
+
const fromPages = raw?.lastUpdated;
|
|
177
|
+
if (typeof fromPages === "string" && fromPages.length > 0) return fromPages;
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
function emitSiteFiles(params) {
|
|
183
|
+
const {
|
|
184
|
+
outDir,
|
|
185
|
+
projectRoot,
|
|
186
|
+
pagesPath,
|
|
187
|
+
routes,
|
|
188
|
+
seoConfig,
|
|
189
|
+
siteUrl,
|
|
190
|
+
exclude = [],
|
|
191
|
+
noindex = [],
|
|
192
|
+
preview = false,
|
|
193
|
+
robots = {},
|
|
194
|
+
llms = true,
|
|
195
|
+
debug = false
|
|
196
|
+
} = params;
|
|
197
|
+
const written = [];
|
|
198
|
+
const effectiveSiteUrl = siteUrl || seoConfig?.global?.siteUrl;
|
|
199
|
+
const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath);
|
|
200
|
+
const lastmod = deriveLastmod(seoConfig, projectRoot, pagesPath);
|
|
201
|
+
let wroteSitemap = false;
|
|
202
|
+
if (!preview) {
|
|
203
|
+
const xml = buildSitemapXml({
|
|
204
|
+
routes,
|
|
205
|
+
siteUrl: effectiveSiteUrl,
|
|
206
|
+
seoConfig,
|
|
207
|
+
exclude,
|
|
208
|
+
noindex,
|
|
209
|
+
excludedGlobs,
|
|
210
|
+
lastmod
|
|
211
|
+
});
|
|
212
|
+
if (xml) {
|
|
213
|
+
fs2.writeFileSync(path2.join(outDir, "sitemap.xml"), xml, "utf8");
|
|
214
|
+
wroteSitemap = true;
|
|
215
|
+
written.push("sitemap.xml");
|
|
216
|
+
if (debug) console.log("[dcs-seo] wrote sitemap.xml");
|
|
217
|
+
} else if (debug) {
|
|
218
|
+
console.log("[dcs-seo] no absolute <loc> derivable; sitemap.xml not written");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (robots.enabled ?? true) {
|
|
222
|
+
const robotsPath = path2.join(outDir, "robots.txt");
|
|
223
|
+
const existing = fs2.existsSync(robotsPath) && !robots.force ? fs2.readFileSync(robotsPath, "utf8") : null;
|
|
224
|
+
const keepHandAuthored = existing !== null && isHandAuthoredRobotsAcceptable(existing);
|
|
225
|
+
if (keepHandAuthored) {
|
|
226
|
+
if (debug)
|
|
227
|
+
console.log("[dcs-seo] dist/robots.txt is hand-authored and meets the floor; leaving it");
|
|
228
|
+
} else {
|
|
229
|
+
const txt = buildRobotsTxt({
|
|
230
|
+
siteUrl: effectiveSiteUrl,
|
|
231
|
+
preview,
|
|
232
|
+
robots,
|
|
233
|
+
hasSitemap: wroteSitemap
|
|
234
|
+
});
|
|
235
|
+
fs2.writeFileSync(robotsPath, txt, "utf8");
|
|
236
|
+
written.push("robots.txt");
|
|
237
|
+
if (debug) {
|
|
238
|
+
console.log(
|
|
239
|
+
existing !== null ? "[dcs-seo] dist/robots.txt failed the factory floor; overwrote with emitted robots" : "[dcs-seo] wrote robots.txt"
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (llms && !preview) {
|
|
245
|
+
const txt = buildLlmsTxt({
|
|
246
|
+
routes,
|
|
247
|
+
siteUrl: effectiveSiteUrl,
|
|
248
|
+
seoConfig,
|
|
249
|
+
exclude,
|
|
250
|
+
noindex,
|
|
251
|
+
excludedGlobs
|
|
252
|
+
});
|
|
253
|
+
if (txt) {
|
|
254
|
+
fs2.writeFileSync(path2.join(outDir, "llms.txt"), txt, "utf8");
|
|
255
|
+
written.push("llms.txt");
|
|
256
|
+
if (debug) console.log("[dcs-seo] wrote llms.txt");
|
|
257
|
+
} else if (debug) {
|
|
258
|
+
console.log("[dcs-seo] nothing indexable; llms.txt not written");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return written;
|
|
262
|
+
}
|
|
263
|
+
function pickPageFaq(content, pageSlug) {
|
|
264
|
+
if (!content) return void 0;
|
|
265
|
+
const fromBlock = (block) => {
|
|
266
|
+
if (!block) return void 0;
|
|
267
|
+
const v = block["faq"];
|
|
268
|
+
return Array.isArray(v) ? v : void 0;
|
|
269
|
+
};
|
|
270
|
+
return fromBlock(content.pages?.[pageSlug]) ?? fromBlock(content.global);
|
|
271
|
+
}
|
|
127
272
|
function emitStaticSeoHtml(params) {
|
|
128
|
-
const {
|
|
273
|
+
const {
|
|
274
|
+
outDir,
|
|
275
|
+
shellHtml,
|
|
276
|
+
routes,
|
|
277
|
+
seoConfig,
|
|
278
|
+
contentConfig,
|
|
279
|
+
exclude = [],
|
|
280
|
+
noindex = [],
|
|
281
|
+
excludedGlobs = [],
|
|
282
|
+
debug = false
|
|
283
|
+
} = params;
|
|
129
284
|
const excludeSet = new Set(exclude);
|
|
130
285
|
const noindexSet = new Set(noindex);
|
|
286
|
+
const siteUrl = seoConfig?.global?.siteUrl ?? "";
|
|
287
|
+
const routeTitles = { "/": "Home" };
|
|
288
|
+
for (const r of routes) {
|
|
289
|
+
if (r.title && r.path) routeTitles[r.path.replace(/\/+$/, "") || "/"] = r.title;
|
|
290
|
+
}
|
|
131
291
|
let written = 0;
|
|
132
292
|
for (const route of routes) {
|
|
133
293
|
if (excludeSet.has(route.path) || route.slug && excludeSet.has(route.slug)) {
|
|
134
294
|
if (debug) console.log(`[dcs-seo] skip (excluded): ${route.path}`);
|
|
135
295
|
continue;
|
|
136
296
|
}
|
|
297
|
+
if (matchesExcludedGlob(route.path, excludedGlobs)) {
|
|
298
|
+
if (debug) console.log(`[dcs-seo] skip (excluded glob): ${route.path}`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
137
301
|
const forceNoindex = noindexSet.has(route.path) || route.slug && noindexSet.has(route.slug);
|
|
302
|
+
const breadcrumbTrail = siteUrl ? breadcrumbTrailFromRoute(route.path, siteUrl, routeTitles) : void 0;
|
|
303
|
+
const reviews = contentConfig ? findReviewItemsForPage(contentConfig, route.slug) : void 0;
|
|
304
|
+
const isBlogPost = /^\/blog\/.+/.test(route.path) && !!route.title;
|
|
305
|
+
const blogMeta = isBlogPost ? { headline: route.title, url: breadcrumbTrail?.[breadcrumbTrail.length - 1]?.item } : void 0;
|
|
306
|
+
const faq = pickPageFaq(contentConfig, route.slug);
|
|
138
307
|
const tags = buildHeadTags(route.slug, route.path, seoConfig, {
|
|
139
308
|
includeKeywords: true,
|
|
140
309
|
fallbackTitle: route.title,
|
|
141
|
-
robots: forceNoindex ? "noindex, nofollow" : void 0
|
|
310
|
+
robots: forceNoindex ? "noindex, nofollow" : void 0,
|
|
311
|
+
emitGraph: true,
|
|
312
|
+
breadcrumbTrail,
|
|
313
|
+
reviews,
|
|
314
|
+
blogMeta,
|
|
315
|
+
faq
|
|
142
316
|
});
|
|
143
317
|
const html = spliceHeadHtml(shellHtml, tags);
|
|
144
318
|
const outFile = routeToOutputFile(outDir, route.path);
|
|
@@ -159,8 +333,16 @@ function dcsSeoPlugin(options = {}) {
|
|
|
159
333
|
debug = false,
|
|
160
334
|
emitStaticHtml = false,
|
|
161
335
|
pagesPath = ".dcs/pages.yaml",
|
|
336
|
+
contentPath = ".dcs/content.yaml",
|
|
162
337
|
exclude = [],
|
|
163
|
-
noindex = []
|
|
338
|
+
noindex = [],
|
|
339
|
+
// Site files default ON whenever per-route HTML emission is on, so the
|
|
340
|
+
// sites already using emitStaticHtml gain sitemap/robots/llms with no edit.
|
|
341
|
+
emitSiteFiles: emitSiteFilesOpt = emitStaticHtml,
|
|
342
|
+
siteUrl,
|
|
343
|
+
robots = {},
|
|
344
|
+
preview = false,
|
|
345
|
+
llms = true
|
|
164
346
|
} = options;
|
|
165
347
|
let resolvedConfig;
|
|
166
348
|
return {
|
|
@@ -203,7 +385,14 @@ function dcsSeoPlugin(options = {}) {
|
|
|
203
385
|
* no-ops. Never throws (so it can never break a production build).
|
|
204
386
|
*/
|
|
205
387
|
writeBundle() {
|
|
206
|
-
if (!emitStaticHtml)
|
|
388
|
+
if (!emitStaticHtml) {
|
|
389
|
+
if (options.emitSiteFiles === true) {
|
|
390
|
+
console.warn(
|
|
391
|
+
"[dcs-seo] emitSiteFiles:true is ignored because emitStaticHtml is false; set emitStaticHtml:true to emit sitemap.xml/robots.txt/llms.txt"
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
207
396
|
try {
|
|
208
397
|
const projectRoot = resolvedConfig?.root || process.cwd();
|
|
209
398
|
const outDir = resolveOutDir(resolvedConfig);
|
|
@@ -232,18 +421,43 @@ function dcsSeoPlugin(options = {}) {
|
|
|
232
421
|
);
|
|
233
422
|
}
|
|
234
423
|
const shellHtml = fs2.readFileSync(shellPath, "utf8");
|
|
424
|
+
const excludedGlobs = loadExcludedGlobs(projectRoot, pagesPath);
|
|
425
|
+
const contentConfig = loadContentConfig(projectRoot, contentPath, debug);
|
|
235
426
|
const written = emitStaticSeoHtml({
|
|
236
427
|
outDir,
|
|
237
428
|
shellHtml,
|
|
238
429
|
routes,
|
|
239
430
|
seoConfig: loaded?.config,
|
|
431
|
+
contentConfig,
|
|
240
432
|
exclude,
|
|
241
433
|
noindex,
|
|
434
|
+
excludedGlobs,
|
|
242
435
|
debug
|
|
243
436
|
});
|
|
244
437
|
if (debug) {
|
|
245
438
|
console.log(`[dcs-seo] emitStaticHtml: wrote ${written} per-route HTML file(s)`);
|
|
246
439
|
}
|
|
440
|
+
if (emitSiteFilesOpt) {
|
|
441
|
+
const siteFiles = emitSiteFiles({
|
|
442
|
+
outDir,
|
|
443
|
+
projectRoot,
|
|
444
|
+
pagesPath,
|
|
445
|
+
routes,
|
|
446
|
+
seoConfig: loaded?.config,
|
|
447
|
+
siteUrl,
|
|
448
|
+
exclude,
|
|
449
|
+
noindex,
|
|
450
|
+
preview,
|
|
451
|
+
robots,
|
|
452
|
+
llms,
|
|
453
|
+
debug
|
|
454
|
+
});
|
|
455
|
+
if (debug) {
|
|
456
|
+
console.log(
|
|
457
|
+
`[dcs-seo] emitSiteFiles: wrote ${siteFiles.length} site file(s): ${siteFiles.join(", ") || "(none)"}`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
247
461
|
} catch (error) {
|
|
248
462
|
console.warn("[dcs-seo] emitStaticHtml failed; build output left unchanged:", error);
|
|
249
463
|
}
|
|
@@ -664,6 +878,6 @@ function responsiveImagePlugin(md) {
|
|
|
664
878
|
};
|
|
665
879
|
}
|
|
666
880
|
|
|
667
|
-
export { dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsPreviewPlugin, dcsSeoPlugin, emitStaticSeoHtml, responsiveImagePlugin };
|
|
881
|
+
export { dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsPreviewPlugin, dcsSeoPlugin, emitSiteFiles, emitStaticSeoHtml, responsiveImagePlugin };
|
|
668
882
|
//# sourceMappingURL=index.js.map
|
|
669
883
|
//# sourceMappingURL=index.js.map
|