@duffcloudservices/cms 0.3.17 → 0.5.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.
@@ -1,9 +1,10 @@
1
- import fs3 from 'fs';
2
- import path from 'path';
1
+ import { buildHeadTags, spliceHeadHtml, loadPagesManifest } from '../chunk-TIGZ7RKI.js';
2
+ export { buildVitePressSeoHead, createSeoTransformPageData, defaultRelativePathToRoute } from '../chunk-TIGZ7RKI.js';
3
+ import fs2 from 'fs';
4
+ import path2 from 'path';
3
5
  import yaml from 'js-yaml';
4
6
  import { defineComponent, h } from 'vue';
5
7
 
6
- // src/plugins/dcsContentPlugin.ts
7
8
  function dcsContentPlugin(options = {}) {
8
9
  const { contentPath = ".dcs/content.yaml", debug = false } = options;
9
10
  let resolvedConfig;
@@ -15,13 +16,13 @@ function dcsContentPlugin(options = {}) {
15
16
  config(config) {
16
17
  const projectRoot = config.root || process.cwd();
17
18
  const possiblePaths = [
18
- path.resolve(projectRoot, contentPath),
19
- path.resolve(projectRoot, "..", contentPath),
20
- path.resolve(process.cwd(), contentPath)
19
+ path2.resolve(projectRoot, contentPath),
20
+ path2.resolve(projectRoot, "..", contentPath),
21
+ path2.resolve(process.cwd(), contentPath)
21
22
  ];
22
23
  let foundPath;
23
24
  for (const testPath of possiblePaths) {
24
- if (fs3.existsSync(testPath)) {
25
+ if (fs2.existsSync(testPath)) {
25
26
  foundPath = testPath;
26
27
  break;
27
28
  }
@@ -39,7 +40,7 @@ function dcsContentPlugin(options = {}) {
39
40
  };
40
41
  }
41
42
  try {
42
- const fileContent = fs3.readFileSync(foundPath, "utf8");
43
+ const fileContent = fs2.readFileSync(foundPath, "utf8");
43
44
  const content = yaml.load(fileContent);
44
45
  if (debug) {
45
46
  console.log(`[dcs-content] Loaded ${foundPath}`);
@@ -65,11 +66,11 @@ function dcsContentPlugin(options = {}) {
65
66
  configureServer(server) {
66
67
  const projectRoot = resolvedConfig?.root || process.cwd();
67
68
  const watchPaths = [
68
- path.resolve(projectRoot, contentPath),
69
- path.resolve(projectRoot, "..", contentPath)
69
+ path2.resolve(projectRoot, contentPath),
70
+ path2.resolve(projectRoot, "..", contentPath)
70
71
  ];
71
72
  watchPaths.forEach((watchPath) => {
72
- if (fs3.existsSync(watchPath)) {
73
+ if (fs2.existsSync(watchPath)) {
73
74
  server.watcher.add(watchPath);
74
75
  server.watcher.on("change", (changedPath) => {
75
76
  if (changedPath === watchPath) {
@@ -84,8 +85,83 @@ function dcsContentPlugin(options = {}) {
84
85
  }
85
86
  };
86
87
  }
88
+ function resolveOutDir(config) {
89
+ const out = config.build?.outDir || "dist";
90
+ return path2.isAbsolute(out) ? out : path2.resolve(config.root || process.cwd(), out);
91
+ }
92
+ function loadSeoConfig(projectRoot, seoPath, debug) {
93
+ const possiblePaths = [
94
+ path2.resolve(projectRoot, seoPath),
95
+ path2.resolve(projectRoot, "..", seoPath),
96
+ path2.resolve(process.cwd(), seoPath)
97
+ ];
98
+ let foundPath;
99
+ for (const testPath of possiblePaths) {
100
+ if (fs2.existsSync(testPath)) {
101
+ foundPath = testPath;
102
+ break;
103
+ }
104
+ }
105
+ if (!foundPath) {
106
+ if (debug) {
107
+ console.log("[dcs-seo] No seo.yaml found at:");
108
+ possiblePaths.forEach((p) => console.log(` - ${p}`));
109
+ console.log("[dcs-seo] Using defaults only");
110
+ }
111
+ return null;
112
+ }
113
+ try {
114
+ const fileContent = fs2.readFileSync(foundPath, "utf8");
115
+ const config = yaml.load(fileContent);
116
+ return { config, foundPath };
117
+ } catch (error) {
118
+ console.warn("[dcs-seo] Failed to parse seo.yaml:", error);
119
+ return null;
120
+ }
121
+ }
122
+ function routeToOutputFile(outDir, routePath) {
123
+ const trimmed = routePath.replace(/^\/+/, "").replace(/\/+$/, "");
124
+ if (trimmed === "") return path2.join(outDir, "index.html");
125
+ return path2.join(outDir, ...trimmed.split("/"), "index.html");
126
+ }
127
+ function emitStaticSeoHtml(params) {
128
+ const { outDir, shellHtml, routes, seoConfig, exclude = [], noindex = [], debug = false } = params;
129
+ const excludeSet = new Set(exclude);
130
+ const noindexSet = new Set(noindex);
131
+ let written = 0;
132
+ for (const route of routes) {
133
+ if (excludeSet.has(route.path) || route.slug && excludeSet.has(route.slug)) {
134
+ if (debug) console.log(`[dcs-seo] skip (excluded): ${route.path}`);
135
+ continue;
136
+ }
137
+ const forceNoindex = noindexSet.has(route.path) || route.slug && noindexSet.has(route.slug);
138
+ const tags = buildHeadTags(route.slug, route.path, seoConfig, {
139
+ includeKeywords: true,
140
+ fallbackTitle: route.title,
141
+ robots: forceNoindex ? "noindex, nofollow" : void 0
142
+ });
143
+ const html = spliceHeadHtml(shellHtml, tags);
144
+ const outFile = routeToOutputFile(outDir, route.path);
145
+ fs2.mkdirSync(path2.dirname(outFile), { recursive: true });
146
+ fs2.writeFileSync(outFile, html, "utf8");
147
+ written++;
148
+ if (debug) {
149
+ console.log(
150
+ `[dcs-seo] wrote ${path2.relative(outDir, outFile)} (title="${tags.title}"${forceNoindex ? ", noindex" : ""})`
151
+ );
152
+ }
153
+ }
154
+ return written;
155
+ }
87
156
  function dcsSeoPlugin(options = {}) {
88
- const { seoPath = ".dcs/seo.yaml", debug = false } = options;
157
+ const {
158
+ seoPath = ".dcs/seo.yaml",
159
+ debug = false,
160
+ emitStaticHtml = false,
161
+ pagesPath = ".dcs/pages.yaml",
162
+ exclude = [],
163
+ noindex = []
164
+ } = options;
89
165
  let resolvedConfig;
90
166
  return {
91
167
  name: "dcs-seo",
@@ -94,62 +170,93 @@ function dcsSeoPlugin(options = {}) {
94
170
  },
95
171
  config(config) {
96
172
  const projectRoot = config.root || process.cwd();
97
- const possiblePaths = [
98
- path.resolve(projectRoot, seoPath),
99
- path.resolve(projectRoot, "..", seoPath),
100
- path.resolve(process.cwd(), seoPath)
101
- ];
102
- let foundPath;
103
- for (const testPath of possiblePaths) {
104
- if (fs3.existsSync(testPath)) {
105
- foundPath = testPath;
106
- break;
107
- }
108
- }
109
- if (!foundPath) {
110
- if (debug) {
111
- console.log("[dcs-seo] No seo.yaml found at:");
112
- possiblePaths.forEach((p) => console.log(` - ${p}`));
113
- console.log("[dcs-seo] Using defaults only");
114
- }
173
+ const loaded = loadSeoConfig(projectRoot, seoPath, debug);
174
+ if (!loaded) {
115
175
  return {
116
176
  define: {
117
177
  __DCS_SEO__: "undefined"
118
178
  }
119
179
  };
120
180
  }
181
+ if (debug) {
182
+ console.log(`[dcs-seo] Loaded ${loaded.foundPath}`);
183
+ console.log(`[dcs-seo] Version: ${loaded.config.version}`);
184
+ console.log(`[dcs-seo] Site Name: ${loaded.config.global?.siteName || "(not set)"}`);
185
+ console.log(
186
+ `[dcs-seo] Pages: ${Object.keys(loaded.config.pages ?? {}).join(", ") || "(none)"}`
187
+ );
188
+ }
189
+ return {
190
+ define: {
191
+ __DCS_SEO__: JSON.stringify(loaded.config)
192
+ }
193
+ };
194
+ },
195
+ /**
196
+ * Build-time static `<head>` emitter (opt-in via `emitStaticHtml`).
197
+ *
198
+ * Runs after the bundle is written so the built `index.html` shell exists
199
+ * on disk. For an SPA, Vite emits a single `index.html`; we fan it out to
200
+ * per-route files with route-specific `<head>` content.
201
+ *
202
+ * Fully defensive: any missing/unparseable input logs a warning and
203
+ * no-ops. Never throws (so it can never break a production build).
204
+ */
205
+ writeBundle() {
206
+ if (!emitStaticHtml) return;
121
207
  try {
122
- const fileContent = fs3.readFileSync(foundPath, "utf8");
123
- const seoConfig = yaml.load(fileContent);
208
+ const projectRoot = resolvedConfig?.root || process.cwd();
209
+ const outDir = resolveOutDir(resolvedConfig);
210
+ if (!fs2.existsSync(outDir)) {
211
+ console.warn(`[dcs-seo] emitStaticHtml: outDir not found (${outDir}); skipping`);
212
+ return;
213
+ }
214
+ const shellPath = path2.join(outDir, "index.html");
215
+ if (!fs2.existsSync(shellPath)) {
216
+ console.warn(
217
+ `[dcs-seo] emitStaticHtml: no index.html in ${outDir}; nothing to emit`
218
+ );
219
+ return;
220
+ }
221
+ const routes = loadPagesManifest(projectRoot, pagesPath, debug);
222
+ if (!routes) {
223
+ console.warn(
224
+ `[dcs-seo] emitStaticHtml: ${pagesPath} missing or empty; skipping per-route emission`
225
+ );
226
+ return;
227
+ }
228
+ const loaded = loadSeoConfig(projectRoot, seoPath, debug);
229
+ if (!loaded) {
230
+ console.warn(
231
+ `[dcs-seo] emitStaticHtml: ${seoPath} missing or unparseable; emitting with defaults only`
232
+ );
233
+ }
234
+ const shellHtml = fs2.readFileSync(shellPath, "utf8");
235
+ const written = emitStaticSeoHtml({
236
+ outDir,
237
+ shellHtml,
238
+ routes,
239
+ seoConfig: loaded?.config,
240
+ exclude,
241
+ noindex,
242
+ debug
243
+ });
124
244
  if (debug) {
125
- console.log(`[dcs-seo] Loaded ${foundPath}`);
126
- console.log(`[dcs-seo] Version: ${seoConfig.version}`);
127
- console.log(`[dcs-seo] Site Name: ${seoConfig.global?.siteName || "(not set)"}`);
128
- console.log(`[dcs-seo] Pages: ${Object.keys(seoConfig.pages ?? {}).join(", ") || "(none)"}`);
245
+ console.log(`[dcs-seo] emitStaticHtml: wrote ${written} per-route HTML file(s)`);
129
246
  }
130
- return {
131
- define: {
132
- __DCS_SEO__: JSON.stringify(seoConfig)
133
- }
134
- };
135
247
  } catch (error) {
136
- console.warn("[dcs-seo] Failed to parse seo.yaml:", error);
137
- return {
138
- define: {
139
- __DCS_SEO__: "undefined"
140
- }
141
- };
248
+ console.warn("[dcs-seo] emitStaticHtml failed; build output left unchanged:", error);
142
249
  }
143
250
  },
144
251
  // Watch for changes in development
145
252
  configureServer(server) {
146
253
  const projectRoot = resolvedConfig?.root || process.cwd();
147
254
  const watchPaths = [
148
- path.resolve(projectRoot, seoPath),
149
- path.resolve(projectRoot, "..", seoPath)
255
+ path2.resolve(projectRoot, seoPath),
256
+ path2.resolve(projectRoot, "..", seoPath)
150
257
  ];
151
258
  watchPaths.forEach((watchPath) => {
152
- if (fs3.existsSync(watchPath)) {
259
+ if (fs2.existsSync(watchPath)) {
153
260
  server.watcher.add(watchPath);
154
261
  server.watcher.on("change", (changedPath) => {
155
262
  if (changedPath === watchPath) {
@@ -290,14 +397,14 @@ function buildPictureElement(entry, alt, extraAttrs, sizes) {
290
397
  }
291
398
  function loadCdnImageMap(projectRoot, relativeMapPath, debug) {
292
399
  const possiblePaths = [
293
- path.resolve(projectRoot, relativeMapPath),
294
- path.resolve(projectRoot, "..", relativeMapPath),
295
- path.resolve(process.cwd(), relativeMapPath)
400
+ path2.resolve(projectRoot, relativeMapPath),
401
+ path2.resolve(projectRoot, "..", relativeMapPath),
402
+ path2.resolve(process.cwd(), relativeMapPath)
296
403
  ];
297
404
  for (const testPath of possiblePaths) {
298
- if (fs3.existsSync(testPath)) {
405
+ if (fs2.existsSync(testPath)) {
299
406
  try {
300
- const raw = fs3.readFileSync(testPath, "utf8");
407
+ const raw = fs2.readFileSync(testPath, "utf8");
301
408
  const data = JSON.parse(raw);
302
409
  const map = /* @__PURE__ */ new Map();
303
410
  for (const entry of data.images) {
@@ -458,13 +565,13 @@ function dcsCdnBuildEnd(options = {}) {
458
565
  return;
459
566
  }
460
567
  const outDir = siteConfig.outDir;
461
- if (!fs3.existsSync(outDir)) return;
568
+ if (!fs2.existsSync(outDir)) return;
462
569
  let filesProcessed = 0;
463
570
  let refsReplaced = 0;
464
571
  function walkDir(dir) {
465
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
572
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
466
573
  for (const entry of entries) {
467
- const fullPath = path.join(dir, entry.name);
574
+ const fullPath = path2.join(dir, entry.name);
468
575
  if (entry.isDirectory()) {
469
576
  walkDir(fullPath);
470
577
  } else if (extensions.some((ext) => entry.name.endsWith(ext))) {
@@ -473,7 +580,7 @@ function dcsCdnBuildEnd(options = {}) {
473
580
  }
474
581
  }
475
582
  function processFile(filePath) {
476
- let content = fs3.readFileSync(filePath, "utf8");
583
+ let content = fs2.readFileSync(filePath, "utf8");
477
584
  if (!pathPrefixes.some((p) => content.includes(p))) return;
478
585
  let changed = false;
479
586
  if (filePath.endsWith(".html")) {
@@ -508,7 +615,7 @@ function dcsCdnBuildEnd(options = {}) {
508
615
  }
509
616
  }
510
617
  if (changed) {
511
- fs3.writeFileSync(filePath, content, "utf8");
618
+ fs2.writeFileSync(filePath, content, "utf8");
512
619
  filesProcessed++;
513
620
  }
514
621
  }
@@ -555,6 +662,6 @@ function responsiveImagePlugin(md) {
555
662
  };
556
663
  }
557
664
 
558
- export { dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsPreviewPlugin, dcsSeoPlugin, responsiveImagePlugin };
665
+ export { dcsCdnBuildEnd, dcsCdnImagePlugin, dcsContentPlugin, dcsEditorPlugin, dcsPreviewPlugin, dcsSeoPlugin, emitStaticSeoHtml, responsiveImagePlugin };
559
666
  //# sourceMappingURL=index.js.map
560
667
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/dcsContentPlugin.ts","../../src/plugins/dcsSeoPlugin.ts","../../src/plugins/dcsEditorPlugin.ts","../../src/plugins/dcsPreviewPlugin.ts","../../src/plugins/dcsCdnImagePlugin.ts","../../src/plugins/responsiveImagePlugin.ts"],"names":["fs","path","yaml","escapeHtml"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * DCS Content Plugin for Vite\n *\n * Reads `.dcs/content.yaml` at build time and injects content\n * as `__DCS_CONTENT__` global variable for use by useTextContent.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsContentPlugin({ debug: true })\n * ]\n * })\n * ```\n *\n * For VitePress:\n * ```typescript\n * // .vitepress/config.ts\n * import { defineConfig } from 'vitepress'\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsContentPlugin()\n * ]\n * }\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { DcsContentFile } from '../types/content'\n\nexport interface DcsContentPluginOptions {\n /** Path to content.yaml relative to project root (default: '.dcs/content.yaml') */\n contentPath?: string\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects .dcs/content.yaml at build time.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsContentPlugin(options: DcsContentPluginOptions = {}): Plugin {\n const { contentPath = '.dcs/content.yaml', debug = false } = options\n\n let resolvedConfig: ResolvedConfig\n\n return {\n name: 'dcs-content',\n\n configResolved(config) {\n resolvedConfig = config\n },\n\n config(config) {\n const projectRoot = config.root || process.cwd()\n\n // Try to find content.yaml in multiple locations\n // VitePress projects have the docs folder as root, so check parent too\n const possiblePaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n path.resolve(process.cwd(), contentPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-content] No content.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-content] Using defaults only')\n }\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n }\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const content = yaml.load(fileContent) as DcsContentFile\n\n if (debug) {\n console.log(`[dcs-content] Loaded ${foundPath}`)\n console.log(`[dcs-content] Version: ${content.version}`)\n console.log(`[dcs-content] Pages: ${Object.keys(content.pages ?? {}).join(', ') || '(none)'}`)\n console.log(`[dcs-content] Global keys: ${Object.keys(content.global ?? {}).length}`)\n }\n\n return {\n define: {\n __DCS_CONTENT__: JSON.stringify(content),\n },\n }\n } catch (error) {\n console.warn('[dcs-content] Failed to parse content.yaml:', error)\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n }\n }\n },\n\n // Watch for changes in development\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n ]\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n\n server.watcher.on('change', (changedPath) => {\n if (changedPath === watchPath) {\n if (debug) {\n console.log('[dcs-content] content.yaml changed, triggering reload')\n }\n server.restart()\n }\n })\n }\n })\n },\n }\n}\n","/**\n * DCS SEO Plugin for Vite\n *\n * Reads `.dcs/seo.yaml` at build time and injects content\n * as `__DCS_SEO__` global variable for use by useSEO.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsSeoPlugin({ debug: true })\n * ]\n * })\n * ```\n *\n * For VitePress:\n * ```typescript\n * // .vitepress/config.ts\n * import { defineConfig } from 'vitepress'\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsSeoPlugin()\n * ]\n * }\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { SeoConfiguration } from '../types/seo'\n\nexport interface DcsSeoPluginOptions {\n /** Path to seo.yaml relative to project root (default: '.dcs/seo.yaml') */\n seoPath?: string\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects .dcs/seo.yaml at build time.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsSeoPlugin(options: DcsSeoPluginOptions = {}): Plugin {\n const { seoPath = '.dcs/seo.yaml', debug = false } = options\n\n let resolvedConfig: ResolvedConfig\n\n return {\n name: 'dcs-seo',\n\n configResolved(config) {\n resolvedConfig = config\n },\n\n config(config) {\n const projectRoot = config.root || process.cwd()\n\n // Try to find seo.yaml in multiple locations\n // VitePress projects have the docs folder as root, so check parent too\n const possiblePaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n path.resolve(process.cwd(), seoPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-seo] No seo.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-seo] Using defaults only')\n }\n return {\n define: {\n __DCS_SEO__: 'undefined',\n },\n }\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const seoConfig = yaml.load(fileContent) as SeoConfiguration\n\n if (debug) {\n console.log(`[dcs-seo] Loaded ${foundPath}`)\n console.log(`[dcs-seo] Version: ${seoConfig.version}`)\n console.log(`[dcs-seo] Site Name: ${seoConfig.global?.siteName || '(not set)'}`)\n console.log(`[dcs-seo] Pages: ${Object.keys(seoConfig.pages ?? {}).join(', ') || '(none)'}`)\n }\n\n return {\n define: {\n __DCS_SEO__: JSON.stringify(seoConfig),\n },\n }\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse seo.yaml:', error)\n return {\n define: {\n __DCS_SEO__: 'undefined',\n },\n }\n }\n },\n\n // Watch for changes in development\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n ]\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n\n server.watcher.on('change', (changedPath) => {\n if (changedPath === watchPath) {\n if (debug) {\n console.log('[dcs-seo] seo.yaml changed, triggering reload')\n }\n server.restart()\n }\n })\n }\n })\n },\n }\n}\n","/**\n * DCS Editor Plugin for Vite\n *\n * Injects the editor bridge script into customer sites when running\n * in portal preview mode (inside the visual editor iframe).\n *\n * The bridge enables:\n * - Inline text editing via contenteditable\n * - Section hover highlights and AI ✨ buttons\n * - postMessage communication with the portal\n *\n * This plugin should only be active in development/preview mode.\n * It's safe to include in production builds — it does nothing unless\n * the page detects it's inside an iframe.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsEditorPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsEditorPlugin()\n * ]\n * })\n * ```\n */\n\nimport type { Plugin } from 'vite'\n\nexport interface DcsEditorPluginOptions {\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects the editor bridge script for portal preview integration.\n *\n * The bridge auto-initializes only when the page detects it's running inside an iframe,\n * so it's safe to include in all builds — it's a no-op in standalone browsing.\n *\n * Uses a virtual module (`/__dcs-editor-bridge.js`) served through Vite's dev server\n * so that bare module specifiers (like `@duffcloudservices/cms/editor`) are properly\n * resolved through Vite's module graph rather than hitting the browser's native ESM\n * resolver, which can't handle bare specifiers.\n */\nexport function dcsEditorPlugin(options: DcsEditorPluginOptions = {}): Plugin {\n const { debug = false } = options\n const VIRTUAL_PATH = '/__dcs-editor-bridge.js'\n const RIBBON_VIRTUAL_PATH = '/__dcs-preview-ribbon.js'\n\n // Captured during configResolved — used to gate HTML injection.\n let isDev = false\n let basePath = '/'\n\n return {\n name: 'dcs-editor-bridge',\n\n configResolved(config) {\n isDev = config.command === 'serve'\n basePath = config.base ?? '/'\n },\n\n resolveId(id) {\n if (id === VIRTUAL_PATH || id === RIBBON_VIRTUAL_PATH) {\n return id\n }\n },\n\n load(id) {\n if (id === VIRTUAL_PATH) {\n return `\nimport { initEditorBridge } from '@duffcloudservices/cms/editor'\n// The bridge only activates inside an iframe (portal preview)\nif (window.parent !== window) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => setTimeout(initEditorBridge, 200))\n } else {\n setTimeout(initEditorBridge, 200)\n }\n}\n`\n }\n\n if (id === RIBBON_VIRTUAL_PATH) {\n return `\nimport { createApp } from 'vue'\nimport PreviewRibbon from '@duffcloudservices/cms/components'\n\nfunction mountRibbon() {\n // Create a detached mount point\n const el = document.createElement('div')\n el.id = 'dcs-preview-ribbon-root'\n document.body.appendChild(el)\n\n const app = createApp(PreviewRibbon)\n app.mount(el)\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', mountRibbon)\n} else {\n mountRibbon()\n}\n`\n }\n },\n\n /**\n * Only inject the editor bridge and ribbon scripts when:\n * 1. Running in dev mode (Vite dev server), AND\n * 2. The app is served at the root path (base === '/')\n *\n * These are virtual modules resolved via absolute paths (e.g.\n * `/__dcs-editor-bridge.js`). When base !== '/', the app is served at a\n * sub-path (e.g. /handyman-bryan/) and the absolute virtual paths are NOT\n * routed to the Vite dev server by the reverse proxy — they hit the main\n * DCS server instead and get redirected to portal.duffcloudservices.com\n * (no CORS headers), causing browser console errors.\n *\n * In production builds, App.vue imports and mounts PreviewRibbon\n * directly, so virtual script injection is always redundant anyway.\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev || basePath !== '/') return html\n\n if (debug) {\n console.log('[dcs-editor] Injecting editor bridge + preview ribbon script tags')\n }\n\n // Inject both the editor bridge and preview ribbon scripts\n const editorTag = `<script type=\"module\" src=\"${VIRTUAL_PATH}\"></script>`\n const ribbonTag = `<script type=\"module\" src=\"${RIBBON_VIRTUAL_PATH}\"></script>`\n\n // Insert before closing </body> tag\n return html.replace('</body>', `${editorTag}\\n${ribbonTag}\\n</body>`)\n },\n },\n }\n}\n","/**\n * DCS Preview Plugin for Vue\n *\n * Registers a supplied ribbon component as a global `DcsPreviewRibbon`\n * component. The ribbon handles its own visibility — it only renders on\n * `preview.duffcloudservices.com` and hides everywhere else (localhost,\n * production domains, and inside the visual page editor iframe).\n *\n * Why does the caller pass the component in? Because this file is compiled\n * by tsup (esbuild) which has no `.vue` SFC loader. Keeping the raw `.vue`\n * import out of the compiled plugins bundle avoids the build error while\n * still letting consumer code (which *does* run through Vite) resolve the\n * SFC at dev/build time.\n *\n * @example VitePress theme/index.ts\n * ```typescript\n * import { dcsPreviewPlugin } from '@duffcloudservices/cms/plugins'\n * import PreviewRibbon from '@duffcloudservices/cms/components'\n *\n * export default {\n * Layout,\n * enhanceApp({ app }) {\n * app.use(dcsPreviewPlugin(PreviewRibbon))\n * }\n * }\n * ```\n */\n\nimport { defineComponent, h, type App, type Component, type Plugin } from 'vue'\n\nexport interface DcsPreviewPluginOptions {\n /**\n * Override the version string displayed on the ribbon.\n * If omitted, the ribbon auto-detects from VITE_SITE_VERSION or the API.\n */\n version?: string | null\n}\n\n/**\n * Creates and returns the DCS Preview plugin.\n *\n * When installed, it registers the supplied ribbon component as a global\n * `DcsPreviewRibbon` component. Add `<DcsPreviewRibbon />` to your root\n * Layout, or use the `dcsEditorPlugin` Vite plugin which injects it via\n * `transformIndexHtml`.\n *\n * @param ribbonComponent - The PreviewRibbon SFC (imported by the consumer)\n * @param options - Optional configuration\n */\nexport function dcsPreviewPlugin(\n ribbonComponent: Component,\n options: DcsPreviewPluginOptions = {},\n): Plugin {\n const Wrapper = defineComponent({\n name: 'DcsPreviewRibbon',\n setup() {\n return () => h(ribbonComponent, { version: options.version ?? null })\n },\n })\n\n return {\n install(app: App) {\n app.component('DcsPreviewRibbon', Wrapper)\n\n if (options.version !== undefined) {\n app.provide('__dcs_preview_version__', options.version)\n }\n },\n }\n}\n","/**\n * DCS CDN Image Plugin for Vite\n *\n * Rewrites local static image references (e.g. `/images/staff/photo.jpg`)\n * to CDN URLs at build time using the `.dcs/cdn-image-map.json` mapping file\n * generated by the `image-migrate adopt` CLI command.\n *\n * For raster images with WebP variants, `<img>` elements are transformed into\n * responsive `<picture>` elements with `srcset` for optimised delivery.\n * SVGs receive a simple URL swap with no variant handling.\n *\n * **In development mode this plugin is a no-op** — local `/images/` paths\n * continue to work via Vite's static asset serving so hot-reload is unaffected.\n *\n * The plugin handles two in-pipeline replacement vectors:\n *\n * 1. **Module transform** (`transform` hook) — rewrites `<img>` tags and\n * string literals in Vue SFCs, TS, JS, CSS, MD, and HTML modules.\n * 2. **Chunk rendering** (`renderChunk` hook) — rewrites image paths in\n * final rendered JS/CSS chunks AFTER Vite's `define` substitution.\n * This catches data injected by `dcsContentPlugin` via `__DCS_CONTENT__`\n * (from `.dcs/content.yaml`) which bypasses the `transform` hook.\n *\n * A third vector — **post-build file processing** — is handled by the\n * companion `dcsCdnBuildEnd` hook, which must be registered separately in\n * VitePress config. VitePress generates HTML *after* both Vite builds\n * complete, so Vite plugin hooks (`closeBundle`, `transformIndexHtml`)\n * cannot catch SSR-rendered `<head>` tags or static `public/` files like\n * `service-worker.js`.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnImagePlugin, dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsCdnImagePlugin()\n * ]\n * },\n * buildEnd: dcsCdnBuildEnd()\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface CdnImageMapVariant {\n suffix: string\n blobPath: string\n cdnUrl: string\n width: number\n height: number\n size: number\n}\n\nexport interface CdnImageMapEntry {\n localPath: string\n blobPath: string\n cdnUrl: string\n fileName: string\n contentType: string\n originalSize: number\n width?: number\n height?: number\n hash: string\n variants?: CdnImageMapVariant[]\n}\n\nexport interface CdnImageMap {\n generated: string\n site: string\n cdnBase: string\n images: CdnImageMapEntry[]\n}\n\nexport interface DcsCdnImagePluginOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement. Each must be a **leading-slash path prefix**\n * that appears in source code (e.g. `/images/`). The `localPath` field in the\n * mapping file is compared *without* a leading slash.\n *\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /**\n * Default `sizes` attribute for responsive `<picture>` elements.\n * Override per-context via the `data-sizes` attribute on the original `<img>`.\n *\n * Default: `'(max-width: 1024px) 100vw, 1024px'`\n */\n defaultSizes?: string\n\n /** Enable debug logging */\n debug?: boolean\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Build a responsive `<picture>` element for a raster image that has\n * WebP variants.\n */\nfunction buildPictureElement(\n entry: CdnImageMapEntry,\n alt: string,\n extraAttrs: string,\n sizes: string\n): string {\n const variants = entry.variants ?? []\n\n // Collect only the display-relevant variants (not thumb, not og)\n const displayVariants = variants.filter(\n (v) => v.suffix === '-sm' || v.suffix === '-md' || v.suffix === '-lg'\n )\n\n if (displayVariants.length === 0) {\n // No display variants — fall back to original CDN URL as plain <img>\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n const srcset = displayVariants\n .map((v) => `${v.cdnUrl} ${v.width}w`)\n .join(', ')\n\n // Pick the -md variant as the default src, or fall through to original\n const mdVariant = variants.find((v) => v.suffix === '-md')\n const fallbackSrc = mdVariant ? mdVariant.cdnUrl : entry.cdnUrl\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"${escapeHtml(sizes)}\" />`,\n ` <img src=\"${escapeHtml(fallbackSrc)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Shared helpers\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Try to find and load the CDN image map from common locations.\n * VitePress nests docs under a subfolder, so we check parent dirs too.\n */\nfunction loadCdnImageMap(\n projectRoot: string,\n relativeMapPath: string,\n debug: boolean\n): Map<string, CdnImageMapEntry> | null {\n const possiblePaths = [\n path.resolve(projectRoot, relativeMapPath),\n path.resolve(projectRoot, '..', relativeMapPath),\n path.resolve(process.cwd(), relativeMapPath),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n try {\n const raw = fs.readFileSync(testPath, 'utf8')\n const data: CdnImageMap = JSON.parse(raw)\n\n const map = new Map<string, CdnImageMapEntry>()\n for (const entry of data.images) {\n // Store both with and without leading slash for flexible matching\n map.set(entry.localPath, entry)\n if (!entry.localPath.startsWith('/')) {\n map.set('/' + entry.localPath, entry)\n }\n }\n\n if (debug) {\n console.log(`[dcs-cdn-image] Loaded ${data.images.length} entries from ${testPath}`)\n }\n return map\n } catch (error) {\n console.warn(`[dcs-cdn-image] Failed to parse ${testPath}:`, error)\n }\n }\n }\n\n if (debug) {\n console.log('[dcs-cdn-image] No cdn-image-map.json found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n }\n return null\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Vite Plugin\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Vite plugin that rewrites static `/images/` paths to CDN URLs at build time.\n */\nexport function dcsCdnImagePlugin(options: DcsCdnImagePluginOptions = {}): Plugin {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n defaultSizes = '(max-width: 1024px) 100vw, 1024px',\n debug = false,\n } = options\n\n // Lookup map built from the JSON file: key = localPath (no leading slash)\n let imageMap: Map<string, CdnImageMapEntry> | null = null\n let isProduction = false\n\n /**\n * Check whether a source code string contains any of the path prefixes\n * we care about. Quick bail-out for files that don't reference images.\n */\n function hasImageReferences(code: string): boolean {\n return pathPrefixes.some((prefix) => code.includes(prefix))\n }\n\n /**\n * Replace image path references in any string value with CDN URLs.\n * Used for code transforms and renderChunk post-processing.\n */\n function replaceImagePaths(str: string): string {\n if (!imageMap || imageMap.size === 0) return str\n let result = str\n for (const prefix of pathPrefixes) {\n // Global replace: matches prefix followed by a relative path bounded\n // by quotes, parens, or whitespace. Allows spaces in filenames.\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n result = result.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${quote}${entry.cdnUrl}`\n })\n }\n return result\n }\n\n /**\n * Replace standalone URL references in JS/TS string literals and Vue template\n * attributes. This handles patterns like:\n * src=\"/images/foo.jpg\"\n * { src: '/images/foo.jpg' }\n * url(/images/foo.svg)\n *\n * For `<img>` tags with raster images that have variants, the entire tag is\n * replaced with a `<picture>` element.\n */\n function transformCode(code: string): string {\n if (!imageMap || imageMap.size === 0) return code\n\n // ── Pass 1: Replace <img> tags that reference mapped raster images ──────\n // Matches: <img ... src=\"/images/foo.jpg\" ... /> or <img ... src=\"/images/foo.jpg\" ... >\n const imgTagRegex = /<img\\b([^>]*?)src=[\"'](\\/(images\\/[^\"']+))[\"']([^>]*?)\\/?>/gi\n code = code.replace(imgTagRegex, (_match, beforeSrc, _srcWithSlash, localPath, afterSrc) => {\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return _match\n\n // Extract alt from the existing attributes\n const altMatch = (beforeSrc + afterSrc).match(/alt=[\"']([^\"']*)[\"']/)\n const alt = altMatch ? altMatch[1] : ''\n\n // Collect other attributes (not src, alt, loading, decoding — we set those)\n const otherAttrs = (beforeSrc + afterSrc)\n .replace(/\\balt=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bloading=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bdecoding=[\"'][^\"']*[\"']/gi, '')\n .trim()\n\n const extraAttrs = otherAttrs ? ' ' + otherAttrs : ''\n\n // Extract custom sizes if specified\n const sizesMatch = (beforeSrc + afterSrc).match(/data-sizes=[\"']([^\"']*)[\"']/)\n const sizes = sizesMatch ? sizesMatch[1] : defaultSizes\n\n // SVGs and images without variants → simple URL swap <img>\n if (!entry.variants || entry.variants.length === 0) {\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n // Raster with variants → <picture>\n return buildPictureElement(entry, alt, extraAttrs, sizes)\n })\n\n // ── Pass 2: Replace remaining string literal / CSS url() references ─────\n code = replaceImagePaths(code)\n\n return code\n }\n\n return {\n name: 'dcs-cdn-image',\n enforce: 'pre',\n\n configResolved(config) {\n isProduction = config.command === 'build'\n if (!isProduction) {\n if (debug) {\n console.log('[dcs-cdn-image] Dev mode — plugin disabled (local images served by Vite)')\n }\n return\n }\n imageMap = loadCdnImageMap(config.root, mapPath, debug)\n },\n\n transform(code, id) {\n // Only run in production builds\n if (!isProduction || !imageMap) return\n\n // Only process relevant file types\n if (!/\\.(vue|ts|tsx|js|jsx|css|scss|md|html)(\\?.*)?$/.test(id)) return\n\n // Quick bail — skip files with no image path references\n if (!hasImageReferences(code)) return\n\n const transformed = transformCode(code)\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n\n /**\n * Replace image paths in final rendered JS/CSS chunks.\n *\n * This fires AFTER Vite's `define` substitution, catching paths that were\n * injected via `define` values (e.g. `__DCS_CONTENT__` set by\n * `dcsContentPlugin` from `.dcs/content.yaml`). Those paths bypass the\n * `transform` hook because `define` replacement happens at bundle time.\n */\n renderChunk(code, _chunk) {\n if (!isProduction || !imageMap) return null\n if (!hasImageReferences(code)) return null\n\n const replaced = replaceImagePaths(code)\n if (replaced !== code) {\n if (debug) {\n console.log(`[dcs-cdn-image] renderChunk: rewrote image paths in ${_chunk.fileName}`)\n }\n return { code: replaced, map: null }\n }\n return null\n },\n\n /**\n * Transform HTML output to catch references injected outside the module\n * pipeline — e.g. VitePress `head` config tags (favicons, OG images).\n *\n * Note: In VitePress, this hook fires for the `index.html` template but\n * NOT for SSR-rendered per-page HTML. Use `dcsCdnBuildEnd` for full\n * coverage of generated HTML files.\n */\n transformIndexHtml(html) {\n if (!isProduction || !imageMap) return html\n if (!hasImageReferences(html)) return html\n\n // Replace href/src/content attributes that reference mapped images\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n html = html.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${pre}${entry.cdnUrl}${post}`\n })\n }\n\n return html\n },\n }\n}\n\n/**\n * Escape a string for use in a RegExp.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// VitePress buildEnd hook\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface DcsCdnBuildEndOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement.\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * File extensions to process in the output directory.\n * Default: `['.html', '.js']`\n */\n extensions?: string[]\n}\n\n/**\n * VitePress `buildEnd` hook factory that post-processes generated HTML and\n * static files in the output directory to replace remaining `/images/` paths\n * with CDN URLs.\n *\n * VitePress generates HTML **after** both Vite builds complete, so Vite\n * plugin hooks (`closeBundle`, `transformIndexHtml`) cannot catch\n * SSR-rendered `<head>` tags (favicons, OG images). This hook runs after all\n * HTML files are written to disk.\n *\n * Also rewrites static files (e.g. `service-worker.js`) copied from\n * `public/` that are not part of Vite's module pipeline.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * buildEnd: dcsCdnBuildEnd({ debug: true })\n * })\n * ```\n */\nexport function dcsCdnBuildEnd(options: DcsCdnBuildEndOptions = {}) {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n debug = false,\n extensions = ['.html', '.js'],\n } = options\n\n return async (siteConfig: { root: string; outDir: string }) => {\n const imageMap = loadCdnImageMap(siteConfig.root, mapPath, debug)\n if (!imageMap) {\n if (debug) {\n console.log('[dcs-cdn-image] buildEnd: no image map found')\n }\n return\n }\n\n const outDir = siteConfig.outDir\n if (!fs.existsSync(outDir)) return\n\n let filesProcessed = 0\n let refsReplaced = 0\n\n function walkDir(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkDir(fullPath)\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n processFile(fullPath)\n }\n }\n }\n\n function processFile(filePath: string) {\n let content = fs.readFileSync(filePath, 'utf8')\n if (!pathPrefixes.some((p) => content.includes(p))) return\n\n let changed = false\n\n if (filePath.endsWith('.html')) {\n // HTML: replace href/src/content attribute values\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n content = content.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const mapEntry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!mapEntry) return match\n changed = true\n refsReplaced++\n return `${pre}${mapEntry.cdnUrl}${post}`\n })\n }\n } else {\n // JS/other: replace quoted string paths\n for (const prefix of pathPrefixes) {\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n content = content.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n changed = true\n refsReplaced++\n return `${quote}${entry.cdnUrl}`\n })\n }\n }\n\n if (changed) {\n fs.writeFileSync(filePath, content, 'utf8')\n filesProcessed++\n }\n }\n\n walkDir(outDir)\n\n if (debug) {\n console.log(\n `[dcs-cdn-image] buildEnd: post-processed ${filesProcessed} files, replaced ${refsReplaced} references`\n )\n }\n }\n}","/**\n * markdown-it plugin that transforms standard `![alt](url)` image syntax\n * into responsive `<picture>` elements when the URL matches the DCS CDN\n * asset pattern.\n *\n * Non-CDN images are rendered with the default image renderer (plain `<img>`).\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { responsiveImagePlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * markdown: {\n * config: (md) => {\n * md.use(responsiveImagePlugin)\n * },\n * },\n * })\n * ```\n *\n * Input markdown:\n * ```md\n * ![Physical therapy session](https://files.duffcloudservices.com/kept/assets/blog/abc-123.jpg)\n * ```\n *\n * Rendered HTML:\n * ```html\n * <picture>\n * <source srcset=\"...abc-123-sm.webp 640w, ...abc-123-md.webp 1024w, ...abc-123-lg.webp 1920w\"\n * type=\"image/webp\"\n * sizes=\"(max-width: 1024px) 100vw, 1024px\" />\n * <img src=\"...abc-123-md.webp\" alt=\"Physical therapy session\" loading=\"lazy\" decoding=\"async\" />\n * </picture>\n * ```\n */\n\nimport type MarkdownIt from 'markdown-it'\n\n/** Matches DCS CDN asset URLs: base path / UUID . extension */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/**\n * Escapes HTML special characters for safe attribute embedding.\n * Falls back to a simple replace chain when `md.utils.escapeHtml` is unavailable.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nexport function responsiveImagePlugin(md: MarkdownIt): void {\n const defaultImageRenderer = md.renderer.rules.image\n\n md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const token = tokens[idx]\n const src = token.attrGet('src') ?? ''\n const alt = token.content ?? ''\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n // Not a CDN image — render normally\n if (defaultImageRenderer) {\n return defaultImageRenderer(tokens, idx, options, env, self)\n }\n return self.renderToken(tokens, idx, options)\n }\n\n const [, basePath, uuid] = match\n\n const srcset = [\n `${basePath}${uuid}-sm.webp 640w`,\n `${basePath}${uuid}-md.webp 1024w`,\n `${basePath}${uuid}-lg.webp 1920w`,\n ].join(', ')\n\n const escapedAlt = escapeHtml(alt)\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"(max-width: 1024px) 100vw, 1024px\" />`,\n ` <img src=\"${basePath}${uuid}-md.webp\" alt=\"${escapedAlt}\" loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/plugins/dcsContentPlugin.ts","../../src/plugins/dcsSeoPlugin.ts","../../src/plugins/dcsEditorPlugin.ts","../../src/plugins/dcsPreviewPlugin.ts","../../src/plugins/dcsCdnImagePlugin.ts","../../src/plugins/responsiveImagePlugin.ts"],"names":["path","fs","yaml","escapeHtml"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * DCS Content Plugin for Vite\n *\n * Reads `.dcs/content.yaml` at build time and injects content\n * as `__DCS_CONTENT__` global variable for use by useTextContent.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsContentPlugin({ debug: true })\n * ]\n * })\n * ```\n *\n * For VitePress:\n * ```typescript\n * // .vitepress/config.ts\n * import { defineConfig } from 'vitepress'\n * import { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsContentPlugin()\n * ]\n * }\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { DcsContentFile } from '../types/content'\n\nexport interface DcsContentPluginOptions {\n /** Path to content.yaml relative to project root (default: '.dcs/content.yaml') */\n contentPath?: string\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects .dcs/content.yaml at build time.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsContentPlugin(options: DcsContentPluginOptions = {}): Plugin {\n const { contentPath = '.dcs/content.yaml', debug = false } = options\n\n let resolvedConfig: ResolvedConfig\n\n return {\n name: 'dcs-content',\n\n configResolved(config) {\n resolvedConfig = config\n },\n\n config(config) {\n const projectRoot = config.root || process.cwd()\n\n // Try to find content.yaml in multiple locations\n // VitePress projects have the docs folder as root, so check parent too\n const possiblePaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n path.resolve(process.cwd(), contentPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-content] No content.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-content] Using defaults only')\n }\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n }\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const content = yaml.load(fileContent) as DcsContentFile\n\n if (debug) {\n console.log(`[dcs-content] Loaded ${foundPath}`)\n console.log(`[dcs-content] Version: ${content.version}`)\n console.log(`[dcs-content] Pages: ${Object.keys(content.pages ?? {}).join(', ') || '(none)'}`)\n console.log(`[dcs-content] Global keys: ${Object.keys(content.global ?? {}).length}`)\n }\n\n return {\n define: {\n __DCS_CONTENT__: JSON.stringify(content),\n },\n }\n } catch (error) {\n console.warn('[dcs-content] Failed to parse content.yaml:', error)\n return {\n define: {\n __DCS_CONTENT__: 'undefined',\n },\n }\n }\n },\n\n // Watch for changes in development\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, contentPath),\n path.resolve(projectRoot, '..', contentPath),\n ]\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n\n server.watcher.on('change', (changedPath) => {\n if (changedPath === watchPath) {\n if (debug) {\n console.log('[dcs-content] content.yaml changed, triggering reload')\n }\n server.restart()\n }\n })\n }\n })\n },\n }\n}\n","/**\n * DCS SEO Plugin for Vite\n *\n * Two responsibilities, both driven by `.dcs/seo.yaml`:\n *\n * 1. **Build-time define** (always on): reads `.dcs/seo.yaml` and injects it as\n * the `__DCS_SEO__` global for the runtime `useSEO` composable.\n *\n * 2. **Static `<head>` emitter** (opt-in via `emitStaticHtml: true`, default\n * OFF): after the bundle is written, reads the SPA's built `index.html`,\n * and for every route in `.dcs/pages.yaml` writes a per-route\n * `dist/<path>/index.html` whose `<head>` carries the resolved title, meta,\n * canonical, Open Graph, Twitter, and JSON-LD tags. This gives a Vue SPA\n * per-route static SEO **without** vite-ssg.\n *\n * VitePress sites already bake SEO via their own config, so they leave this\n * option OFF and are completely unaffected.\n *\n * @example Runtime define only (default — safe for VitePress)\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [dcsSeoPlugin({ debug: true })]\n * })\n * ```\n *\n * @example Vue SPA with per-route static <head> emission\n * ```typescript\n * // vite.config.ts\n * import { dcsSeoPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsSeoPlugin({\n * emitStaticHtml: true, // turn the emitter ON\n * pagesPath: '.dcs/pages.yaml', // route manifest (default)\n * noindex: ['account', 'projects'], // robots: noindex,nofollow\n * exclude: ['/preview'], // skip these routes entirely\n * })\n * ]\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport yaml from 'js-yaml'\nimport type { Plugin, ResolvedConfig } from 'vite'\nimport type { SeoConfiguration } from '../types/seo'\nimport { buildHeadTags } from '../seo/headTags'\nimport { spliceHeadHtml } from '../seo/spliceHeadHtml'\nimport { loadPagesManifest, type PageRouteEntry } from '../seo/pagesManifest'\n\nexport interface DcsSeoPluginOptions {\n /** Path to seo.yaml relative to project root (default: '.dcs/seo.yaml') */\n seoPath?: string\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * Opt-in: emit per-route static `<head>` (meta + JSON-LD) into the built\n * `dist/` at the end of the build. Default `false` — VitePress sites and any\n * site that bakes its own SEO are unaffected when this is off.\n */\n emitStaticHtml?: boolean\n\n /**\n * Path to the route manifest relative to project root, used only when\n * `emitStaticHtml` is true. Default `'.dcs/pages.yaml'`.\n */\n pagesPath?: string\n\n /**\n * Routes to skip entirely (no per-route HTML written). Matched against the\n * route `path` (e.g. `'/preview'`) OR the route `slug` (e.g. `'account'`).\n */\n exclude?: string[]\n\n /**\n * Routes that should receive `robots: noindex, nofollow`. Matched against the\n * route `path` OR `slug`. The home route (`/`) still overwrites\n * `dist/index.html`.\n */\n noindex?: string[]\n}\n\n/** Resolve the SPA output directory from Vite config (absolute path). */\nfunction resolveOutDir(config: ResolvedConfig): string {\n const out = config.build?.outDir || 'dist'\n return path.isAbsolute(out) ? out : path.resolve(config.root || process.cwd(), out)\n}\n\n/**\n * Load + parse seo.yaml from the usual `.dcs` locations.\n * Returns `{ config, foundPath }` or `null` (missing/unparseable).\n */\nfunction loadSeoConfig(\n projectRoot: string,\n seoPath: string,\n debug: boolean\n): { config: SeoConfiguration; foundPath: string } | null {\n const possiblePaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n path.resolve(process.cwd(), seoPath),\n ]\n\n let foundPath: string | undefined\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n foundPath = testPath\n break\n }\n }\n\n if (!foundPath) {\n if (debug) {\n console.log('[dcs-seo] No seo.yaml found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n console.log('[dcs-seo] Using defaults only')\n }\n return null\n }\n\n try {\n const fileContent = fs.readFileSync(foundPath, 'utf8')\n const config = yaml.load(fileContent) as SeoConfiguration\n return { config, foundPath }\n } catch (error) {\n console.warn('[dcs-seo] Failed to parse seo.yaml:', error)\n return null\n }\n}\n\n/**\n * Normalise a route path to the on-disk output file:\n * '/' -> '<outDir>/index.html'\n * '/services' -> '<outDir>/services/index.html'\n * '/blog/my-post' -> '<outDir>/blog/my-post/index.html'\n * '/services/' -> '<outDir>/services/index.html'\n */\nfunction routeToOutputFile(outDir: string, routePath: string): string {\n const trimmed = routePath.replace(/^\\/+/, '').replace(/\\/+$/, '')\n if (trimmed === '') return path.join(outDir, 'index.html')\n return path.join(outDir, ...trimmed.split('/'), 'index.html')\n}\n\n/**\n * Core emitter, separated from Vite so it is unit-testable.\n *\n * For each route: build the head tags from the shared resolver, splice them\n * into the shell HTML, and write `dist/<path>/index.html`. Returns the number\n * of files written. Pure aside from `fs` writes — deterministic + idempotent.\n */\nexport function emitStaticSeoHtml(params: {\n outDir: string\n shellHtml: string\n routes: PageRouteEntry[]\n seoConfig: SeoConfiguration | undefined\n exclude?: string[]\n noindex?: string[]\n debug?: boolean\n}): number {\n const { outDir, shellHtml, routes, seoConfig, exclude = [], noindex = [], debug = false } = params\n\n const excludeSet = new Set(exclude)\n const noindexSet = new Set(noindex)\n\n let written = 0\n for (const route of routes) {\n if (excludeSet.has(route.path) || (route.slug && excludeSet.has(route.slug))) {\n if (debug) console.log(`[dcs-seo] skip (excluded): ${route.path}`)\n continue\n }\n\n const forceNoindex =\n noindexSet.has(route.path) || (route.slug && noindexSet.has(route.slug))\n\n const tags = buildHeadTags(route.slug, route.path, seoConfig, {\n includeKeywords: true,\n fallbackTitle: route.title,\n robots: forceNoindex ? 'noindex, nofollow' : undefined,\n })\n\n const html = spliceHeadHtml(shellHtml, tags)\n const outFile = routeToOutputFile(outDir, route.path)\n\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, html, 'utf8')\n written++\n\n if (debug) {\n console.log(\n `[dcs-seo] wrote ${path.relative(outDir, outFile)} (title=\"${tags.title}\"${\n forceNoindex ? ', noindex' : ''\n })`\n )\n }\n }\n\n return written\n}\n\n/**\n * Vite plugin that injects .dcs/seo.yaml at build time and, optionally, emits\n * per-route static `<head>` HTML for SPA SEO.\n *\n * @param options - Plugin configuration\n * @returns Vite plugin\n */\nexport function dcsSeoPlugin(options: DcsSeoPluginOptions = {}): Plugin {\n const {\n seoPath = '.dcs/seo.yaml',\n debug = false,\n emitStaticHtml = false,\n pagesPath = '.dcs/pages.yaml',\n exclude = [],\n noindex = [],\n } = options\n\n let resolvedConfig: ResolvedConfig\n\n return {\n name: 'dcs-seo',\n\n configResolved(config) {\n resolvedConfig = config\n },\n\n config(config) {\n const projectRoot = config.root || process.cwd()\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n return {\n define: {\n __DCS_SEO__: 'undefined',\n },\n }\n }\n\n if (debug) {\n console.log(`[dcs-seo] Loaded ${loaded.foundPath}`)\n console.log(`[dcs-seo] Version: ${loaded.config.version}`)\n console.log(`[dcs-seo] Site Name: ${loaded.config.global?.siteName || '(not set)'}`)\n console.log(\n `[dcs-seo] Pages: ${Object.keys(loaded.config.pages ?? {}).join(', ') || '(none)'}`\n )\n }\n\n return {\n define: {\n __DCS_SEO__: JSON.stringify(loaded.config),\n },\n }\n },\n\n /**\n * Build-time static `<head>` emitter (opt-in via `emitStaticHtml`).\n *\n * Runs after the bundle is written so the built `index.html` shell exists\n * on disk. For an SPA, Vite emits a single `index.html`; we fan it out to\n * per-route files with route-specific `<head>` content.\n *\n * Fully defensive: any missing/unparseable input logs a warning and\n * no-ops. Never throws (so it can never break a production build).\n */\n writeBundle() {\n if (!emitStaticHtml) return\n\n try {\n const projectRoot = resolvedConfig?.root || process.cwd()\n const outDir = resolveOutDir(resolvedConfig)\n\n if (!fs.existsSync(outDir)) {\n console.warn(`[dcs-seo] emitStaticHtml: outDir not found (${outDir}); skipping`)\n return\n }\n\n const shellPath = path.join(outDir, 'index.html')\n if (!fs.existsSync(shellPath)) {\n console.warn(\n `[dcs-seo] emitStaticHtml: no index.html in ${outDir}; nothing to emit`\n )\n return\n }\n\n const routes = loadPagesManifest(projectRoot, pagesPath, debug)\n if (!routes) {\n console.warn(\n `[dcs-seo] emitStaticHtml: ${pagesPath} missing or empty; skipping per-route emission`\n )\n return\n }\n\n const loaded = loadSeoConfig(projectRoot, seoPath, debug)\n if (!loaded) {\n // seo.yaml is optional for emission — routes still get baked with\n // global/runtime defaults (which here means just the shell title and\n // a derived canonical-less head). Warn so the operator knows.\n console.warn(\n `[dcs-seo] emitStaticHtml: ${seoPath} missing or unparseable; emitting with defaults only`\n )\n }\n\n const shellHtml = fs.readFileSync(shellPath, 'utf8')\n\n const written = emitStaticSeoHtml({\n outDir,\n shellHtml,\n routes,\n seoConfig: loaded?.config,\n exclude,\n noindex,\n debug,\n })\n\n if (debug) {\n console.log(`[dcs-seo] emitStaticHtml: wrote ${written} per-route HTML file(s)`)\n }\n } catch (error) {\n // Never break the build on SEO emission failure.\n console.warn('[dcs-seo] emitStaticHtml failed; build output left unchanged:', error)\n }\n },\n\n // Watch for changes in development\n configureServer(server) {\n const projectRoot = resolvedConfig?.root || process.cwd()\n\n const watchPaths = [\n path.resolve(projectRoot, seoPath),\n path.resolve(projectRoot, '..', seoPath),\n ]\n\n watchPaths.forEach((watchPath) => {\n if (fs.existsSync(watchPath)) {\n server.watcher.add(watchPath)\n\n server.watcher.on('change', (changedPath) => {\n if (changedPath === watchPath) {\n if (debug) {\n console.log('[dcs-seo] seo.yaml changed, triggering reload')\n }\n server.restart()\n }\n })\n }\n })\n },\n }\n}\n","/**\n * DCS Editor Plugin for Vite\n *\n * Injects the editor bridge script into customer sites when running\n * in portal preview mode (inside the visual editor iframe).\n *\n * The bridge enables:\n * - Inline text editing via contenteditable\n * - Section hover highlights and AI ✨ buttons\n * - postMessage communication with the portal\n *\n * This plugin should only be active in development/preview mode.\n * It's safe to include in production builds — it does nothing unless\n * the page detects it's inside an iframe.\n *\n * @example\n * ```typescript\n * // vite.config.ts\n * import { dcsEditorPlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * plugins: [\n * dcsEditorPlugin()\n * ]\n * })\n * ```\n */\n\nimport type { Plugin } from 'vite'\n\nexport interface DcsEditorPluginOptions {\n /** Enable debug logging */\n debug?: boolean\n}\n\n/**\n * Vite plugin that injects the editor bridge script for portal preview integration.\n *\n * The bridge auto-initializes only when the page detects it's running inside an iframe,\n * so it's safe to include in all builds — it's a no-op in standalone browsing.\n *\n * Uses a virtual module (`/__dcs-editor-bridge.js`) served through Vite's dev server\n * so that bare module specifiers (like `@duffcloudservices/cms/editor`) are properly\n * resolved through Vite's module graph rather than hitting the browser's native ESM\n * resolver, which can't handle bare specifiers.\n */\nexport function dcsEditorPlugin(options: DcsEditorPluginOptions = {}): Plugin {\n const { debug = false } = options\n const VIRTUAL_PATH = '/__dcs-editor-bridge.js'\n const RIBBON_VIRTUAL_PATH = '/__dcs-preview-ribbon.js'\n\n // Captured during configResolved — used to gate HTML injection.\n let isDev = false\n let basePath = '/'\n\n return {\n name: 'dcs-editor-bridge',\n\n configResolved(config) {\n isDev = config.command === 'serve'\n basePath = config.base ?? '/'\n },\n\n resolveId(id) {\n if (id === VIRTUAL_PATH || id === RIBBON_VIRTUAL_PATH) {\n return id\n }\n },\n\n load(id) {\n if (id === VIRTUAL_PATH) {\n return `\nimport { initEditorBridge } from '@duffcloudservices/cms/editor'\n// The bridge only activates inside an iframe (portal preview)\nif (window.parent !== window) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => setTimeout(initEditorBridge, 200))\n } else {\n setTimeout(initEditorBridge, 200)\n }\n}\n`\n }\n\n if (id === RIBBON_VIRTUAL_PATH) {\n return `\nimport { createApp } from 'vue'\nimport PreviewRibbon from '@duffcloudservices/cms/components'\n\nfunction mountRibbon() {\n // Create a detached mount point\n const el = document.createElement('div')\n el.id = 'dcs-preview-ribbon-root'\n document.body.appendChild(el)\n\n const app = createApp(PreviewRibbon)\n app.mount(el)\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', mountRibbon)\n} else {\n mountRibbon()\n}\n`\n }\n },\n\n /**\n * Only inject the editor bridge and ribbon scripts when:\n * 1. Running in dev mode (Vite dev server), AND\n * 2. The app is served at the root path (base === '/')\n *\n * These are virtual modules resolved via absolute paths (e.g.\n * `/__dcs-editor-bridge.js`). When base !== '/', the app is served at a\n * sub-path (e.g. /handyman-bryan/) and the absolute virtual paths are NOT\n * routed to the Vite dev server by the reverse proxy — they hit the main\n * DCS server instead and get redirected to portal.duffcloudservices.com\n * (no CORS headers), causing browser console errors.\n *\n * In production builds, App.vue imports and mounts PreviewRibbon\n * directly, so virtual script injection is always redundant anyway.\n */\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n if (!isDev || basePath !== '/') return html\n\n if (debug) {\n console.log('[dcs-editor] Injecting editor bridge + preview ribbon script tags')\n }\n\n // Inject both the editor bridge and preview ribbon scripts\n const editorTag = `<script type=\"module\" src=\"${VIRTUAL_PATH}\"></script>`\n const ribbonTag = `<script type=\"module\" src=\"${RIBBON_VIRTUAL_PATH}\"></script>`\n\n // Insert before closing </body> tag\n return html.replace('</body>', `${editorTag}\\n${ribbonTag}\\n</body>`)\n },\n },\n }\n}\n","/**\n * DCS Preview Plugin for Vue\n *\n * Registers a supplied ribbon component as a global `DcsPreviewRibbon`\n * component. The ribbon handles its own visibility — it only renders on\n * `preview.duffcloudservices.com` and hides everywhere else (localhost,\n * production domains, and inside the visual page editor iframe).\n *\n * Why does the caller pass the component in? Because this file is compiled\n * by tsup (esbuild) which has no `.vue` SFC loader. Keeping the raw `.vue`\n * import out of the compiled plugins bundle avoids the build error while\n * still letting consumer code (which *does* run through Vite) resolve the\n * SFC at dev/build time.\n *\n * @example VitePress theme/index.ts\n * ```typescript\n * import { dcsPreviewPlugin } from '@duffcloudservices/cms/plugins'\n * import PreviewRibbon from '@duffcloudservices/cms/components'\n *\n * export default {\n * Layout,\n * enhanceApp({ app }) {\n * app.use(dcsPreviewPlugin(PreviewRibbon))\n * }\n * }\n * ```\n */\n\nimport { defineComponent, h, type App, type Component, type Plugin } from 'vue'\n\nexport interface DcsPreviewPluginOptions {\n /**\n * Override the version string displayed on the ribbon.\n * If omitted, the ribbon auto-detects from VITE_SITE_VERSION or the API.\n */\n version?: string | null\n}\n\n/**\n * Creates and returns the DCS Preview plugin.\n *\n * When installed, it registers the supplied ribbon component as a global\n * `DcsPreviewRibbon` component. Add `<DcsPreviewRibbon />` to your root\n * Layout, or use the `dcsEditorPlugin` Vite plugin which injects it via\n * `transformIndexHtml`.\n *\n * @param ribbonComponent - The PreviewRibbon SFC (imported by the consumer)\n * @param options - Optional configuration\n */\nexport function dcsPreviewPlugin(\n ribbonComponent: Component,\n options: DcsPreviewPluginOptions = {},\n): Plugin {\n const Wrapper = defineComponent({\n name: 'DcsPreviewRibbon',\n setup() {\n return () => h(ribbonComponent, { version: options.version ?? null })\n },\n })\n\n return {\n install(app: App) {\n app.component('DcsPreviewRibbon', Wrapper)\n\n if (options.version !== undefined) {\n app.provide('__dcs_preview_version__', options.version)\n }\n },\n }\n}\n","/**\n * DCS CDN Image Plugin for Vite\n *\n * Rewrites local static image references (e.g. `/images/staff/photo.jpg`)\n * to CDN URLs at build time using the `.dcs/cdn-image-map.json` mapping file\n * generated by the `image-migrate adopt` CLI command.\n *\n * For raster images with WebP variants, `<img>` elements are transformed into\n * responsive `<picture>` elements with `srcset` for optimised delivery.\n * SVGs receive a simple URL swap with no variant handling.\n *\n * **In development mode this plugin is a no-op** — local `/images/` paths\n * continue to work via Vite's static asset serving so hot-reload is unaffected.\n *\n * The plugin handles two in-pipeline replacement vectors:\n *\n * 1. **Module transform** (`transform` hook) — rewrites `<img>` tags and\n * string literals in Vue SFCs, TS, JS, CSS, MD, and HTML modules.\n * 2. **Chunk rendering** (`renderChunk` hook) — rewrites image paths in\n * final rendered JS/CSS chunks AFTER Vite's `define` substitution.\n * This catches data injected by `dcsContentPlugin` via `__DCS_CONTENT__`\n * (from `.dcs/content.yaml`) which bypasses the `transform` hook.\n *\n * A third vector — **post-build file processing** — is handled by the\n * companion `dcsCdnBuildEnd` hook, which must be registered separately in\n * VitePress config. VitePress generates HTML *after* both Vite builds\n * complete, so Vite plugin hooks (`closeBundle`, `transformIndexHtml`)\n * cannot catch SSR-rendered `<head>` tags or static `public/` files like\n * `service-worker.js`.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnImagePlugin, dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * vite: {\n * plugins: [\n * dcsCdnImagePlugin()\n * ]\n * },\n * buildEnd: dcsCdnBuildEnd()\n * })\n * ```\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport type { Plugin } from 'vite'\n\n// ────────────────────────────────────────────────────────────────────────────\n// Types\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface CdnImageMapVariant {\n suffix: string\n blobPath: string\n cdnUrl: string\n width: number\n height: number\n size: number\n}\n\nexport interface CdnImageMapEntry {\n localPath: string\n blobPath: string\n cdnUrl: string\n fileName: string\n contentType: string\n originalSize: number\n width?: number\n height?: number\n hash: string\n variants?: CdnImageMapVariant[]\n}\n\nexport interface CdnImageMap {\n generated: string\n site: string\n cdnBase: string\n images: CdnImageMapEntry[]\n}\n\nexport interface DcsCdnImagePluginOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement. Each must be a **leading-slash path prefix**\n * that appears in source code (e.g. `/images/`). The `localPath` field in the\n * mapping file is compared *without* a leading slash.\n *\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /**\n * Default `sizes` attribute for responsive `<picture>` elements.\n * Override per-context via the `data-sizes` attribute on the original `<img>`.\n *\n * Default: `'(max-width: 1024px) 100vw, 1024px'`\n */\n defaultSizes?: string\n\n /** Enable debug logging */\n debug?: boolean\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\n/**\n * Build a responsive `<picture>` element for a raster image that has\n * WebP variants.\n */\nfunction buildPictureElement(\n entry: CdnImageMapEntry,\n alt: string,\n extraAttrs: string,\n sizes: string\n): string {\n const variants = entry.variants ?? []\n\n // Collect only the display-relevant variants (not thumb, not og)\n const displayVariants = variants.filter(\n (v) => v.suffix === '-sm' || v.suffix === '-md' || v.suffix === '-lg'\n )\n\n if (displayVariants.length === 0) {\n // No display variants — fall back to original CDN URL as plain <img>\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n const srcset = displayVariants\n .map((v) => `${v.cdnUrl} ${v.width}w`)\n .join(', ')\n\n // Pick the -md variant as the default src, or fall through to original\n const mdVariant = variants.find((v) => v.suffix === '-md')\n const fallbackSrc = mdVariant ? mdVariant.cdnUrl : entry.cdnUrl\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"${escapeHtml(sizes)}\" />`,\n ` <img src=\"${escapeHtml(fallbackSrc)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Shared helpers\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Try to find and load the CDN image map from common locations.\n * VitePress nests docs under a subfolder, so we check parent dirs too.\n */\nfunction loadCdnImageMap(\n projectRoot: string,\n relativeMapPath: string,\n debug: boolean\n): Map<string, CdnImageMapEntry> | null {\n const possiblePaths = [\n path.resolve(projectRoot, relativeMapPath),\n path.resolve(projectRoot, '..', relativeMapPath),\n path.resolve(process.cwd(), relativeMapPath),\n ]\n\n for (const testPath of possiblePaths) {\n if (fs.existsSync(testPath)) {\n try {\n const raw = fs.readFileSync(testPath, 'utf8')\n const data: CdnImageMap = JSON.parse(raw)\n\n const map = new Map<string, CdnImageMapEntry>()\n for (const entry of data.images) {\n // Store both with and without leading slash for flexible matching\n map.set(entry.localPath, entry)\n if (!entry.localPath.startsWith('/')) {\n map.set('/' + entry.localPath, entry)\n }\n }\n\n if (debug) {\n console.log(`[dcs-cdn-image] Loaded ${data.images.length} entries from ${testPath}`)\n }\n return map\n } catch (error) {\n console.warn(`[dcs-cdn-image] Failed to parse ${testPath}:`, error)\n }\n }\n }\n\n if (debug) {\n console.log('[dcs-cdn-image] No cdn-image-map.json found at:')\n possiblePaths.forEach((p) => console.log(` - ${p}`))\n }\n return null\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Vite Plugin\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Vite plugin that rewrites static `/images/` paths to CDN URLs at build time.\n */\nexport function dcsCdnImagePlugin(options: DcsCdnImagePluginOptions = {}): Plugin {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n defaultSizes = '(max-width: 1024px) 100vw, 1024px',\n debug = false,\n } = options\n\n // Lookup map built from the JSON file: key = localPath (no leading slash)\n let imageMap: Map<string, CdnImageMapEntry> | null = null\n let isProduction = false\n\n /**\n * Check whether a source code string contains any of the path prefixes\n * we care about. Quick bail-out for files that don't reference images.\n */\n function hasImageReferences(code: string): boolean {\n return pathPrefixes.some((prefix) => code.includes(prefix))\n }\n\n /**\n * Replace image path references in any string value with CDN URLs.\n * Used for code transforms and renderChunk post-processing.\n */\n function replaceImagePaths(str: string): string {\n if (!imageMap || imageMap.size === 0) return str\n let result = str\n for (const prefix of pathPrefixes) {\n // Global replace: matches prefix followed by a relative path bounded\n // by quotes, parens, or whitespace. Allows spaces in filenames.\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n result = result.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${quote}${entry.cdnUrl}`\n })\n }\n return result\n }\n\n /**\n * Replace standalone URL references in JS/TS string literals and Vue template\n * attributes. This handles patterns like:\n * src=\"/images/foo.jpg\"\n * { src: '/images/foo.jpg' }\n * url(/images/foo.svg)\n *\n * For `<img>` tags with raster images that have variants, the entire tag is\n * replaced with a `<picture>` element.\n */\n function transformCode(code: string): string {\n if (!imageMap || imageMap.size === 0) return code\n\n // ── Pass 1: Replace <img> tags that reference mapped raster images ──────\n // Matches: <img ... src=\"/images/foo.jpg\" ... /> or <img ... src=\"/images/foo.jpg\" ... >\n const imgTagRegex = /<img\\b([^>]*?)src=[\"'](\\/(images\\/[^\"']+))[\"']([^>]*?)\\/?>/gi\n code = code.replace(imgTagRegex, (_match, beforeSrc, _srcWithSlash, localPath, afterSrc) => {\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return _match\n\n // Extract alt from the existing attributes\n const altMatch = (beforeSrc + afterSrc).match(/alt=[\"']([^\"']*)[\"']/)\n const alt = altMatch ? altMatch[1] : ''\n\n // Collect other attributes (not src, alt, loading, decoding — we set those)\n const otherAttrs = (beforeSrc + afterSrc)\n .replace(/\\balt=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bloading=[\"'][^\"']*[\"']/gi, '')\n .replace(/\\bdecoding=[\"'][^\"']*[\"']/gi, '')\n .trim()\n\n const extraAttrs = otherAttrs ? ' ' + otherAttrs : ''\n\n // Extract custom sizes if specified\n const sizesMatch = (beforeSrc + afterSrc).match(/data-sizes=[\"']([^\"']*)[\"']/)\n const sizes = sizesMatch ? sizesMatch[1] : defaultSizes\n\n // SVGs and images without variants → simple URL swap <img>\n if (!entry.variants || entry.variants.length === 0) {\n return `<img src=\"${escapeHtml(entry.cdnUrl)}\" alt=\"${escapeHtml(alt)}\"${extraAttrs} loading=\"lazy\" decoding=\"async\" />`\n }\n\n // Raster with variants → <picture>\n return buildPictureElement(entry, alt, extraAttrs, sizes)\n })\n\n // ── Pass 2: Replace remaining string literal / CSS url() references ─────\n code = replaceImagePaths(code)\n\n return code\n }\n\n return {\n name: 'dcs-cdn-image',\n enforce: 'pre',\n\n configResolved(config) {\n isProduction = config.command === 'build'\n if (!isProduction) {\n if (debug) {\n console.log('[dcs-cdn-image] Dev mode — plugin disabled (local images served by Vite)')\n }\n return\n }\n imageMap = loadCdnImageMap(config.root, mapPath, debug)\n },\n\n transform(code, id) {\n // Only run in production builds\n if (!isProduction || !imageMap) return\n\n // Only process relevant file types\n if (!/\\.(vue|ts|tsx|js|jsx|css|scss|md|html)(\\?.*)?$/.test(id)) return\n\n // Quick bail — skip files with no image path references\n if (!hasImageReferences(code)) return\n\n const transformed = transformCode(code)\n if (transformed !== code) {\n return { code: transformed, map: null }\n }\n },\n\n /**\n * Replace image paths in final rendered JS/CSS chunks.\n *\n * This fires AFTER Vite's `define` substitution, catching paths that were\n * injected via `define` values (e.g. `__DCS_CONTENT__` set by\n * `dcsContentPlugin` from `.dcs/content.yaml`). Those paths bypass the\n * `transform` hook because `define` replacement happens at bundle time.\n */\n renderChunk(code, _chunk) {\n if (!isProduction || !imageMap) return null\n if (!hasImageReferences(code)) return null\n\n const replaced = replaceImagePaths(code)\n if (replaced !== code) {\n if (debug) {\n console.log(`[dcs-cdn-image] renderChunk: rewrote image paths in ${_chunk.fileName}`)\n }\n return { code: replaced, map: null }\n }\n return null\n },\n\n /**\n * Transform HTML output to catch references injected outside the module\n * pipeline — e.g. VitePress `head` config tags (favicons, OG images).\n *\n * Note: In VitePress, this hook fires for the `index.html` template but\n * NOT for SSR-rendered per-page HTML. Use `dcsCdnBuildEnd` for full\n * coverage of generated HTML files.\n */\n transformIndexHtml(html) {\n if (!isProduction || !imageMap) return html\n if (!hasImageReferences(html)) return html\n\n // Replace href/src/content attributes that reference mapped images\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n html = html.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n return `${pre}${entry.cdnUrl}${post}`\n })\n }\n\n return html\n },\n }\n}\n\n/**\n * Escape a string for use in a RegExp.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// VitePress buildEnd hook\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface DcsCdnBuildEndOptions {\n /** Path to cdn-image-map.json relative to project root (default: '.dcs/cdn-image-map.json') */\n mapPath?: string\n\n /**\n * Patterns to match for replacement.\n * Default: `['/images/']`\n */\n pathPrefixes?: string[]\n\n /** Enable debug logging */\n debug?: boolean\n\n /**\n * File extensions to process in the output directory.\n * Default: `['.html', '.js']`\n */\n extensions?: string[]\n}\n\n/**\n * VitePress `buildEnd` hook factory that post-processes generated HTML and\n * static files in the output directory to replace remaining `/images/` paths\n * with CDN URLs.\n *\n * VitePress generates HTML **after** both Vite builds complete, so Vite\n * plugin hooks (`closeBundle`, `transformIndexHtml`) cannot catch\n * SSR-rendered `<head>` tags (favicons, OG images). This hook runs after all\n * HTML files are written to disk.\n *\n * Also rewrites static files (e.g. `service-worker.js`) copied from\n * `public/` that are not part of Vite's module pipeline.\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { dcsCdnBuildEnd } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * buildEnd: dcsCdnBuildEnd({ debug: true })\n * })\n * ```\n */\nexport function dcsCdnBuildEnd(options: DcsCdnBuildEndOptions = {}) {\n const {\n mapPath = '.dcs/cdn-image-map.json',\n pathPrefixes = ['/images/'],\n debug = false,\n extensions = ['.html', '.js'],\n } = options\n\n return async (siteConfig: { root: string; outDir: string }) => {\n const imageMap = loadCdnImageMap(siteConfig.root, mapPath, debug)\n if (!imageMap) {\n if (debug) {\n console.log('[dcs-cdn-image] buildEnd: no image map found')\n }\n return\n }\n\n const outDir = siteConfig.outDir\n if (!fs.existsSync(outDir)) return\n\n let filesProcessed = 0\n let refsReplaced = 0\n\n function walkDir(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n walkDir(fullPath)\n } else if (extensions.some((ext) => entry.name.endsWith(ext))) {\n processFile(fullPath)\n }\n }\n }\n\n function processFile(filePath: string) {\n let content = fs.readFileSync(filePath, 'utf8')\n if (!pathPrefixes.some((p) => content.includes(p))) return\n\n let changed = false\n\n if (filePath.endsWith('.html')) {\n // HTML: replace href/src/content attribute values\n for (const prefix of pathPrefixes) {\n const attrRegex = new RegExp(\n `((?:href|src|content)=[\"'])${escapeRegex(prefix)}([^\"']+)([\"'])`,\n 'gi'\n )\n content = content.replace(attrRegex, (match, pre, relPath, post) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const mapEntry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!mapEntry) return match\n changed = true\n refsReplaced++\n return `${pre}${mapEntry.cdnUrl}${post}`\n })\n }\n } else {\n // JS/other: replace quoted string paths\n for (const prefix of pathPrefixes) {\n const regex = new RegExp(\n `([\"'\\`(])${escapeRegex(prefix)}([^\"'\\`()]+)`,\n 'g'\n )\n content = content.replace(regex, (match, quote, relPath) => {\n const localPath = prefix.replace(/^\\//, '') + relPath\n const entry = imageMap!.get(localPath) || imageMap!.get('/' + localPath)\n if (!entry) return match\n changed = true\n refsReplaced++\n return `${quote}${entry.cdnUrl}`\n })\n }\n }\n\n if (changed) {\n fs.writeFileSync(filePath, content, 'utf8')\n filesProcessed++\n }\n }\n\n walkDir(outDir)\n\n if (debug) {\n console.log(\n `[dcs-cdn-image] buildEnd: post-processed ${filesProcessed} files, replaced ${refsReplaced} references`\n )\n }\n }\n}","/**\n * markdown-it plugin that transforms standard `![alt](url)` image syntax\n * into responsive `<picture>` elements when the URL matches the DCS CDN\n * asset pattern.\n *\n * Non-CDN images are rendered with the default image renderer (plain `<img>`).\n *\n * @example\n * ```ts\n * // .vitepress/config.ts\n * import { responsiveImagePlugin } from '@duffcloudservices/cms/plugins'\n *\n * export default defineConfig({\n * markdown: {\n * config: (md) => {\n * md.use(responsiveImagePlugin)\n * },\n * },\n * })\n * ```\n *\n * Input markdown:\n * ```md\n * ![Physical therapy session](https://files.duffcloudservices.com/kept/assets/blog/abc-123.jpg)\n * ```\n *\n * Rendered HTML:\n * ```html\n * <picture>\n * <source srcset=\"...abc-123-sm.webp 640w, ...abc-123-md.webp 1024w, ...abc-123-lg.webp 1920w\"\n * type=\"image/webp\"\n * sizes=\"(max-width: 1024px) 100vw, 1024px\" />\n * <img src=\"...abc-123-md.webp\" alt=\"Physical therapy session\" loading=\"lazy\" decoding=\"async\" />\n * </picture>\n * ```\n */\n\nimport type MarkdownIt from 'markdown-it'\n\n/** Matches DCS CDN asset URLs: base path / UUID . extension */\nconst CDN_PATTERN = /^(https?:\\/\\/files\\.[^/]+\\/[^/]+\\/assets\\/(?:[^/]+\\/)*)([a-f0-9-]+)\\.(\\w+)$/\n\n/**\n * Escapes HTML special characters for safe attribute embedding.\n * Falls back to a simple replace chain when `md.utils.escapeHtml` is unavailable.\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n}\n\nexport function responsiveImagePlugin(md: MarkdownIt): void {\n const defaultImageRenderer = md.renderer.rules.image\n\n md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const token = tokens[idx]\n const src = token.attrGet('src') ?? ''\n const alt = token.content ?? ''\n\n const match = src.match(CDN_PATTERN)\n if (!match) {\n // Not a CDN image — render normally\n if (defaultImageRenderer) {\n return defaultImageRenderer(tokens, idx, options, env, self)\n }\n return self.renderToken(tokens, idx, options)\n }\n\n const [, basePath, uuid] = match\n\n const srcset = [\n `${basePath}${uuid}-sm.webp 640w`,\n `${basePath}${uuid}-md.webp 1024w`,\n `${basePath}${uuid}-lg.webp 1920w`,\n ].join(', ')\n\n const escapedAlt = escapeHtml(alt)\n\n return [\n '<picture>',\n ` <source srcset=\"${srcset}\" type=\"image/webp\" sizes=\"(max-width: 1024px) 100vw, 1024px\" />`,\n ` <img src=\"${basePath}${uuid}-md.webp\" alt=\"${escapedAlt}\" loading=\"lazy\" decoding=\"async\" />`,\n '</picture>',\n ].join('\\n')\n }\n}\n"]}