@duffcloudservices/cms-angular 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,145 +1,145 @@
1
- # @duffcloudservices/cms-angular
2
-
3
- Angular services and build scripts for DCS CMS integration.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pnpm add @duffcloudservices/cms-angular
9
- ```
10
-
11
- ## Usage
12
-
13
- ### 1. Add Provider
14
-
15
- ```typescript
16
- // app.config.ts
17
- import { ApplicationConfig } from '@angular/core'
18
- import { provideDcs } from '@duffcloudservices/cms-angular'
19
-
20
- export const appConfig: ApplicationConfig = {
21
- providers: [
22
- provideDcs(),
23
- ],
24
- }
25
- ```
26
-
27
- ### 2. Use in Components
28
-
29
- ```typescript
30
- // pages/home.component.ts
31
- import { Component, inject, OnInit } from '@angular/core'
32
- import { DcsContentService, DcsSeoService } from '@duffcloudservices/cms-angular'
33
-
34
- @Component({
35
- selector: 'app-home',
36
- template: `
37
- <h1>{{ title }}</h1>
38
- <p>{{ subtitle }}</p>
39
- `,
40
- })
41
- export class HomeComponent implements OnInit {
42
- private content = inject(DcsContentService)
43
- private seo = inject(DcsSeoService)
44
-
45
- get title() {
46
- return this.content.t('home', 'hero.title', 'Welcome')
47
- }
48
-
49
- get subtitle() {
50
- return this.content.t('home', 'hero.subtitle', 'Build amazing things')
51
- }
52
-
53
- ngOnInit() {
54
- this.seo.setPageSeo('home')
55
- }
56
- }
57
- ```
58
-
59
- ### 3. Post-Build Injection
60
-
61
- After running `ng build`, inject the DCS content into index.html:
62
-
63
- ```bash
64
- npx dcs-inject dist/my-app
65
- ```
66
-
67
- Or add to your package.json scripts:
68
-
69
- ```json
70
- {
71
- "scripts": {
72
- "build": "ng build && dcs-inject dist/my-app"
73
- }
74
- }
75
- ```
76
-
77
- ### DCS Configuration Files
78
-
79
- Create `.dcs/content.yaml`:
80
-
81
- ```yaml
82
- version: 1
83
- global:
84
- nav.home: Home
85
- footer.copyright: © 2026 Company
86
- pages:
87
- home:
88
- hero.title: Welcome to Our Site
89
- hero.subtitle: Build something amazing
90
- ```
91
-
92
- Create `.dcs/seo.yaml`:
93
-
94
- ```yaml
95
- version: 1
96
- global:
97
- siteName: My Site
98
- defaultTitle: My Site
99
- titleTemplate: "%s | My Site"
100
- pages:
101
- home:
102
- title: Welcome
103
- description: The homepage of My Site
104
- ```
105
-
106
- ## API
107
-
108
- ### DcsContentService
109
-
110
- Service for text content management.
111
-
112
- **Methods:**
113
- - `t(page, key, fallback?)`: Get text by page and key
114
- - `getPageContent(page)`: Get all content for a page
115
- - `fetchRuntimeContent(siteSlug, apiBaseUrl?)`: Fetch runtime content (premium)
116
-
117
- **Signals:**
118
- - `content`: Current content configuration
119
- - `isLoading`: Loading state
120
- - `error`: Error message
121
-
122
- ### DcsSeoService
123
-
124
- Service for SEO meta tag management.
125
-
126
- **Methods:**
127
- - `setPageSeo(page, overrides?)`: Apply SEO for a page
128
- - `getSeoConfig()`: Get raw SEO configuration
129
-
130
- ### CLI: dcs-inject
131
-
132
- Inject content into Angular build output.
133
-
134
- ```bash
135
- npx dcs-inject <dist-path> [options]
136
-
137
- Options:
138
- --content-path Path to content.yaml (default: .dcs/content.yaml)
139
- --seo-path Path to seo.yaml (default: .dcs/seo.yaml)
140
- --index-file Index file name (default: index.html)
141
- ```
142
-
143
- ## License
144
-
145
- MIT
1
+ # @duffcloudservices/cms-angular
2
+
3
+ Angular services and build scripts for DCS CMS integration.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @duffcloudservices/cms-angular
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### 1. Add Provider
14
+
15
+ ```typescript
16
+ // app.config.ts
17
+ import { ApplicationConfig } from '@angular/core'
18
+ import { provideDcs } from '@duffcloudservices/cms-angular'
19
+
20
+ export const appConfig: ApplicationConfig = {
21
+ providers: [
22
+ provideDcs(),
23
+ ],
24
+ }
25
+ ```
26
+
27
+ ### 2. Use in Components
28
+
29
+ ```typescript
30
+ // pages/home.component.ts
31
+ import { Component, inject, OnInit } from '@angular/core'
32
+ import { DcsContentService, DcsSeoService } from '@duffcloudservices/cms-angular'
33
+
34
+ @Component({
35
+ selector: 'app-home',
36
+ template: `
37
+ <h1>{{ title }}</h1>
38
+ <p>{{ subtitle }}</p>
39
+ `,
40
+ })
41
+ export class HomeComponent implements OnInit {
42
+ private content = inject(DcsContentService)
43
+ private seo = inject(DcsSeoService)
44
+
45
+ get title() {
46
+ return this.content.t('home', 'hero.title', 'Welcome')
47
+ }
48
+
49
+ get subtitle() {
50
+ return this.content.t('home', 'hero.subtitle', 'Build amazing things')
51
+ }
52
+
53
+ ngOnInit() {
54
+ this.seo.setPageSeo('home')
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### 3. Post-Build Injection
60
+
61
+ After running `ng build`, inject the DCS content into index.html:
62
+
63
+ ```bash
64
+ npx dcs-inject dist/my-app
65
+ ```
66
+
67
+ Or add to your package.json scripts:
68
+
69
+ ```json
70
+ {
71
+ "scripts": {
72
+ "build": "ng build && dcs-inject dist/my-app"
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### DCS Configuration Files
78
+
79
+ Create `.dcs/content.yaml`:
80
+
81
+ ```yaml
82
+ version: 1
83
+ global:
84
+ nav.home: Home
85
+ footer.copyright: © 2026 Company
86
+ pages:
87
+ home:
88
+ hero.title: Welcome to Our Site
89
+ hero.subtitle: Build something amazing
90
+ ```
91
+
92
+ Create `.dcs/seo.yaml`:
93
+
94
+ ```yaml
95
+ version: 1
96
+ global:
97
+ siteName: My Site
98
+ defaultTitle: My Site
99
+ titleTemplate: "%s | My Site"
100
+ pages:
101
+ home:
102
+ title: Welcome
103
+ description: The homepage of My Site
104
+ ```
105
+
106
+ ## API
107
+
108
+ ### DcsContentService
109
+
110
+ Service for text content management.
111
+
112
+ **Methods:**
113
+ - `t(page, key, fallback?)`: Get text by page and key
114
+ - `getPageContent(page)`: Get all content for a page
115
+ - `fetchRuntimeContent(siteSlug, apiBaseUrl?)`: Fetch runtime content (premium)
116
+
117
+ **Signals:**
118
+ - `content`: Current content configuration
119
+ - `isLoading`: Loading state
120
+ - `error`: Error message
121
+
122
+ ### DcsSeoService
123
+
124
+ Service for SEO meta tag management.
125
+
126
+ **Methods:**
127
+ - `setPageSeo(page, overrides?)`: Apply SEO for a page
128
+ - `getSeoConfig()`: Get raw SEO configuration
129
+
130
+ ### CLI: dcs-inject
131
+
132
+ Inject content into Angular build output.
133
+
134
+ ```bash
135
+ npx dcs-inject <dist-path> [options]
136
+
137
+ Options:
138
+ --content-path Path to content.yaml (default: .dcs/content.yaml)
139
+ --seo-path Path to seo.yaml (default: .dcs/seo.yaml)
140
+ --index-file Index file name (default: index.html)
141
+ ```
142
+
143
+ ## License
144
+
145
+ MIT
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/services/content.service.ts","../src/services/seo.service.ts","../src/providers.ts"],"names":["Injectable"],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * DcsContentService for Angular\r\n *\r\n * Provides text content management with build-time injection support and optional\r\n * runtime API overrides for DCS-managed customer sites.\r\n */\r\n\r\nimport { Injectable, signal, computed } from '@angular/core'\r\nimport {\r\n resolveTextKey,\r\n getPageContent,\r\n fetchRuntimeContent,\r\n type ContentConfiguration,\r\n} from '@duffcloudservices/cms-core/browser'\r\n\r\n// Declare the global injected by build script\r\ndeclare const __DCS_CONTENT__: ContentConfiguration | undefined\r\n\r\n/**\r\n * Angular service for DCS text content management.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class DcsContentService {\r\n /** Build-time content (injected via index.html script) */\r\n private readonly buildContent = this.getBuildTimeContent()\r\n\r\n /** Runtime content (fetched if runtime mode enabled) */\r\n private readonly runtimeContent = signal<ContentConfiguration | null>(null)\r\n\r\n /** Loading state for runtime fetch */\r\n readonly isLoading = signal(false)\r\n\r\n /** Error state for runtime fetch */\r\n readonly error = signal<string | null>(null)\r\n\r\n /** Current content (runtime overrides build-time) */\r\n readonly content = computed(() => this.runtimeContent() ?? this.buildContent)\r\n\r\n /**\r\n * Safely get build-time content configuration.\r\n */\r\n private getBuildTimeContent(): ContentConfiguration | undefined {\r\n try {\r\n if (__DCS_CONTENT__ !== undefined && __DCS_CONTENT__ !== null) {\r\n return __DCS_CONTENT__\r\n }\r\n } catch {\r\n // __DCS_CONTENT__ not defined\r\n }\r\n return undefined\r\n }\r\n\r\n /**\r\n * Get text for a specific page and key.\r\n *\r\n * @param page - Page slug matching entry in content.yaml\r\n * @param key - Text key to retrieve\r\n * @param fallback - Fallback value if key not found\r\n * @returns The resolved text value\r\n */\r\n t(page: string, key: string, fallback?: string): string {\r\n const c = this.content()\r\n if (!c) return fallback ?? key\r\n return resolveTextKey(c, page, key) ?? fallback ?? key\r\n }\r\n\r\n /**\r\n * Get all content for a specific page (merged global + page).\r\n *\r\n * @param page - Page slug\r\n * @returns Merged content object\r\n */\r\n getPageContent(page: string): Record<string, string> {\r\n const c = this.content()\r\n if (!c) return {}\r\n return getPageContent(c, page)\r\n }\r\n\r\n /**\r\n * Fetch runtime content from DCS API (premium tier only).\r\n *\r\n * @param siteSlug - The site's slug identifier\r\n * @param apiBaseUrl - Optional API base URL override\r\n */\r\n async fetchRuntimeContent(siteSlug: string, apiBaseUrl?: string): Promise<void> {\r\n this.isLoading.set(true)\r\n this.error.set(null)\r\n\r\n try {\r\n const data = await fetchRuntimeContent(siteSlug, { apiBaseUrl })\r\n if (data) {\r\n this.runtimeContent.set(data)\r\n }\r\n } catch (err) {\r\n this.error.set(err instanceof Error ? err.message : 'Failed to fetch content')\r\n } finally {\r\n this.isLoading.set(false)\r\n }\r\n }\r\n}\r\n","/**\r\n * DcsSeoService for Angular\r\n *\r\n * Applies SEO meta tags to the document head based on .dcs/seo.yaml configuration.\r\n */\r\n\r\nimport { Injectable, inject } from '@angular/core'\r\nimport { Meta, Title } from '@angular/platform-browser'\r\nimport {\r\n resolveSeoForPage,\r\n buildMetaTags,\r\n type SeoConfiguration,\r\n type ResolvedSeo,\r\n} from '@duffcloudservices/cms-core/browser'\r\n\r\n// Declare the global injected by build script\r\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\r\n\r\n/**\r\n * Angular service for DCS SEO management.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class DcsSeoService {\r\n private readonly meta = inject(Meta)\r\n private readonly title = inject(Title)\r\n\r\n /** Build-time SEO config (injected via index.html script) */\r\n private readonly seoConfig = this.getBuildTimeSeo()\r\n\r\n /**\r\n * Safely get build-time SEO configuration.\r\n */\r\n private getBuildTimeSeo(): SeoConfiguration | undefined {\r\n try {\r\n if (__DCS_SEO__ !== undefined && __DCS_SEO__ !== null) {\r\n return __DCS_SEO__\r\n }\r\n } catch {\r\n // __DCS_SEO__ not defined\r\n }\r\n return undefined\r\n }\r\n\r\n /**\r\n * Apply SEO for a specific page.\r\n *\r\n * @param page - Page slug matching entry in seo.yaml\r\n * @param overrides - Optional overrides for title, description, or image\r\n * @returns The resolved SEO configuration\r\n */\r\n setPageSeo(\r\n page: string,\r\n overrides?: { title?: string; description?: string; image?: string }\r\n ): ResolvedSeo | null {\r\n if (!this.seoConfig) return null\r\n\r\n const seo = resolveSeoForPage(this.seoConfig, page)\r\n const resolved: ResolvedSeo = {\r\n ...seo,\r\n ...(overrides?.title && { title: overrides.title }),\r\n ...(overrides?.description && { description: overrides.description }),\r\n ...(overrides?.image && { image: overrides.image }),\r\n }\r\n\r\n // Set document title\r\n this.title.setTitle(resolved.title)\r\n\r\n // Build and apply meta tags\r\n const tags = buildMetaTags(resolved)\r\n for (const tag of tags) {\r\n if (tag.property) {\r\n this.meta.updateTag({ property: tag.property, content: tag.content })\r\n } else if (tag.name) {\r\n this.meta.updateTag({ name: tag.name, content: tag.content })\r\n }\r\n }\r\n\r\n // Handle canonical link\r\n if (resolved.canonical) {\r\n this.updateCanonicalLink(resolved.canonical)\r\n }\r\n\r\n return resolved\r\n }\r\n\r\n /**\r\n * Update or create canonical link element.\r\n */\r\n private updateCanonicalLink(href: string): void {\r\n let link = document.querySelector('link[rel=\"canonical\"]')\r\n if (!link) {\r\n link = document.createElement('link')\r\n link.setAttribute('rel', 'canonical')\r\n document.head.appendChild(link)\r\n }\r\n link.setAttribute('href', href)\r\n }\r\n\r\n /**\r\n * Get the raw SEO configuration.\r\n */\r\n getSeoConfig(): SeoConfiguration | undefined {\r\n return this.seoConfig\r\n }\r\n}\r\n","/**\r\n * Angular providers for DCS CMS integration.\r\n */\r\n\r\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core'\r\nimport { DcsContentService } from './services/content.service'\r\nimport { DcsSeoService } from './services/seo.service'\r\n\r\n/**\r\n * Provide DCS CMS services for Angular application.\r\n *\r\n * @example\r\n * ```typescript\r\n * // app.config.ts\r\n * import { ApplicationConfig } from '@angular/core'\r\n * import { provideDcs } from '@duffcloudservices/cms-angular'\r\n *\r\n * export const appConfig: ApplicationConfig = {\r\n * providers: [\r\n * provideDcs(),\r\n * ],\r\n * }\r\n * ```\r\n */\r\nexport function provideDcs(): EnvironmentProviders {\r\n return makeEnvironmentProviders([DcsContentService, DcsSeoService])\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/services/content.service.ts","../src/services/seo.service.ts","../src/providers.ts"],"names":["Injectable"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * DcsContentService for Angular\n *\n * Provides text content management with build-time injection support and optional\n * runtime API overrides for DCS-managed customer sites.\n */\n\nimport { Injectable, signal, computed } from '@angular/core'\nimport {\n resolveTextKey,\n getPageContent,\n fetchRuntimeContent,\n type ContentConfiguration,\n} from '@duffcloudservices/cms-core/browser'\n\n// Declare the global injected by build script\ndeclare const __DCS_CONTENT__: ContentConfiguration | undefined\n\n/**\n * Angular service for DCS text content management.\n */\n@Injectable({ providedIn: 'root' })\nexport class DcsContentService {\n /** Build-time content (injected via index.html script) */\n private readonly buildContent = this.getBuildTimeContent()\n\n /** Runtime content (fetched if runtime mode enabled) */\n private readonly runtimeContent = signal<ContentConfiguration | null>(null)\n\n /** Loading state for runtime fetch */\n readonly isLoading = signal(false)\n\n /** Error state for runtime fetch */\n readonly error = signal<string | null>(null)\n\n /** Current content (runtime overrides build-time) */\n readonly content = computed(() => this.runtimeContent() ?? this.buildContent)\n\n /**\n * Safely get build-time content configuration.\n */\n private getBuildTimeContent(): ContentConfiguration | undefined {\n try {\n if (__DCS_CONTENT__ !== undefined && __DCS_CONTENT__ !== null) {\n return __DCS_CONTENT__\n }\n } catch {\n // __DCS_CONTENT__ not defined\n }\n return undefined\n }\n\n /**\n * Get text for a specific page and key.\n *\n * @param page - Page slug matching entry in content.yaml\n * @param key - Text key to retrieve\n * @param fallback - Fallback value if key not found\n * @returns The resolved text value\n */\n t(page: string, key: string, fallback?: string): string {\n const c = this.content()\n if (!c) return fallback ?? key\n return resolveTextKey(c, page, key) ?? fallback ?? key\n }\n\n /**\n * Get all content for a specific page (merged global + page).\n *\n * @param page - Page slug\n * @returns Merged content object\n */\n getPageContent(page: string): Record<string, string> {\n const c = this.content()\n if (!c) return {}\n return getPageContent(c, page)\n }\n\n /**\n * Fetch runtime content from DCS API (premium tier only).\n *\n * @param siteSlug - The site's slug identifier\n * @param apiBaseUrl - Optional API base URL override\n */\n async fetchRuntimeContent(siteSlug: string, apiBaseUrl?: string): Promise<void> {\n this.isLoading.set(true)\n this.error.set(null)\n\n try {\n const data = await fetchRuntimeContent(siteSlug, { apiBaseUrl })\n if (data) {\n this.runtimeContent.set(data)\n }\n } catch (err) {\n this.error.set(err instanceof Error ? err.message : 'Failed to fetch content')\n } finally {\n this.isLoading.set(false)\n }\n }\n}\n","/**\n * DcsSeoService for Angular\n *\n * Applies SEO meta tags to the document head based on .dcs/seo.yaml configuration.\n */\n\nimport { Injectable, inject } from '@angular/core'\nimport { Meta, Title } from '@angular/platform-browser'\nimport {\n resolveSeoForPage,\n buildMetaTags,\n type SeoConfiguration,\n type ResolvedSeo,\n} from '@duffcloudservices/cms-core/browser'\n\n// Declare the global injected by build script\ndeclare const __DCS_SEO__: SeoConfiguration | undefined\n\n/**\n * Angular service for DCS SEO management.\n */\n@Injectable({ providedIn: 'root' })\nexport class DcsSeoService {\n private readonly meta = inject(Meta)\n private readonly title = inject(Title)\n\n /** Build-time SEO config (injected via index.html script) */\n private readonly seoConfig = this.getBuildTimeSeo()\n\n /**\n * Safely get build-time SEO configuration.\n */\n private getBuildTimeSeo(): SeoConfiguration | undefined {\n try {\n if (__DCS_SEO__ !== undefined && __DCS_SEO__ !== null) {\n return __DCS_SEO__\n }\n } catch {\n // __DCS_SEO__ not defined\n }\n return undefined\n }\n\n /**\n * Apply SEO for a specific page.\n *\n * @param page - Page slug matching entry in seo.yaml\n * @param overrides - Optional overrides for title, description, or image\n * @returns The resolved SEO configuration\n */\n setPageSeo(\n page: string,\n overrides?: { title?: string; description?: string; image?: string }\n ): ResolvedSeo | null {\n if (!this.seoConfig) return null\n\n const seo = resolveSeoForPage(this.seoConfig, page)\n const resolved: ResolvedSeo = {\n ...seo,\n ...(overrides?.title && { title: overrides.title }),\n ...(overrides?.description && { description: overrides.description }),\n ...(overrides?.image && { image: overrides.image }),\n }\n\n // Set document title\n this.title.setTitle(resolved.title)\n\n // Build and apply meta tags\n const tags = buildMetaTags(resolved)\n for (const tag of tags) {\n if (tag.property) {\n this.meta.updateTag({ property: tag.property, content: tag.content })\n } else if (tag.name) {\n this.meta.updateTag({ name: tag.name, content: tag.content })\n }\n }\n\n // Handle canonical link\n if (resolved.canonical) {\n this.updateCanonicalLink(resolved.canonical)\n }\n\n return resolved\n }\n\n /**\n * Update or create canonical link element.\n */\n private updateCanonicalLink(href: string): void {\n let link = document.querySelector('link[rel=\"canonical\"]')\n if (!link) {\n link = document.createElement('link')\n link.setAttribute('rel', 'canonical')\n document.head.appendChild(link)\n }\n link.setAttribute('href', href)\n }\n\n /**\n * Get the raw SEO configuration.\n */\n getSeoConfig(): SeoConfiguration | undefined {\n return this.seoConfig\n }\n}\n","/**\n * Angular providers for DCS CMS integration.\n */\n\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core'\nimport { DcsContentService } from './services/content.service'\nimport { DcsSeoService } from './services/seo.service'\n\n/**\n * Provide DCS CMS services for Angular application.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { ApplicationConfig } from '@angular/core'\n * import { provideDcs } from '@duffcloudservices/cms-angular'\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideDcs(),\n * ],\n * }\n * ```\n */\nexport function provideDcs(): EnvironmentProviders {\n return makeEnvironmentProviders([DcsContentService, DcsSeoService])\n}\n"]}
File without changes
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/scripts/inject-content.ts"],"names":[],"mappings":";;;;;AAwBA,eAAsB,aAAA,CACpB,QAAA,EACA,OAAA,GAII,EAAC,EACU;AACf,EAAA,MAAM;AAAA,IACJ,WAAA,GAAc,mBAAA;AAAA,IACd,OAAA,GAAU,eAAA;AAAA,IACV,SAAA,GAAY;AAAA,GACd,GAAI,OAAA;AAEJ,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,SAAS,CAAA;AAG/C,EAAA,IAAI,CAAC,EAAA,CAAG,UAAA,CAAW,SAAS,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,CAAA,CAAE,CAAA;AAAA,EACxD;AAGA,EAAA,IAAI,WAAA,GAAc,WAAA;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAM,eAAA,CAAgB,WAAW,CAAA;AACjD,IAAA,WAAA,GAAc,IAAA,CAAK,UAAU,OAAO,CAAA;AACpC,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAAoC,WAAW,CAAA,CAAE,CAAA;AAAA,EAC/D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,sCAAA,EAAyC,WAAW,CAAA,CAAE,CAAA;AAAA,EACpE;AAGA,EAAA,IAAI,OAAA,GAAU,WAAA;AACd,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAY,OAAO,CAAA;AACrC,IAAA,OAAA,GAAU,IAAA,CAAK,UAAU,GAAG,CAAA;AAC5B,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,oCAAA,EAAuC,OAAO,CAAA,CAAE,CAAA;AAAA,EAC9D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kCAAA,EAAqC,OAAO,CAAA,CAAE,CAAA;AAAA,EAC5D;AAGA,EAAA,IAAI,IAAA,GAAO,EAAA,CAAG,YAAA,CAAa,SAAA,EAAW,MAAM,CAAA;AAG5C,EAAA,MAAM,SAAA,GAAY;AAAA;AAAA,2BAAA,EAES,WAAW,CAAA;AAAA,uBAAA,EACf,OAAO,CAAA;AAAA;AAAA,CAAA;AAK9B,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,EAAG;AAC5B,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,CAAA,EAAG,SAAS,CAAA,OAAA,CAAS,CAAA;AAAA,EACtD,CAAA,MAAO;AAEL,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,KAAA,CAAO,CAAA;AAAA,EAClD;AAGA,EAAA,EAAA,CAAG,aAAA,CAAc,WAAW,IAAI,CAAA;AAChC,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,mCAAA,EAAsC,SAAS,CAAA,CAAE,CAAA;AAC/D;AAGA,IAAI,QAAQ,IAAA,CAAK,CAAC,CAAA,EAAG,QAAA,CAAS,gBAAgB,CAAA,EAAG;AAC/C,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AAC/B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAA,CAAQ,MAAM,+BAA+B,CAAA;AAC7C,IAAA,OAAA,CAAQ,MAAM,iCAAiC,CAAA;AAC/C,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,aAAA,CAAc,QAAQ,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AACrC,IAAA,OAAA,CAAQ,KAAA,CAAM,qBAAA,EAAuB,GAAA,CAAI,OAAO,CAAA;AAChD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB,CAAC,CAAA;AACH","file":"inject-content.js","sourcesContent":["#!/usr/bin/env node\r\n/**\r\n * Post-build script to inject DCS content into Angular's index.html\r\n *\r\n * Usage:\r\n * npx dcs-inject dist/my-app\r\n * node node_modules/@duffcloudservices/cms-angular/dist/scripts/inject-content.js dist/my-app\r\n *\r\n * This script:\r\n * 1. Reads .dcs/content.yaml and .dcs/seo.yaml from the project root\r\n * 2. Injects them as __DCS_CONTENT__ and __DCS_SEO__ globals in index.html\r\n * 3. Writes the modified index.html back to the dist folder\r\n */\r\n\r\nimport fs from 'node:fs'\r\nimport path from 'node:path'\r\nimport { loadContentYaml, loadSeoYaml } from '@duffcloudservices/cms-core'\r\n\r\n/**\r\n * Inject DCS content into an Angular build's index.html\r\n *\r\n * @param distPath - Path to the Angular build output (e.g., 'dist/my-app')\r\n * @param options - Optional configuration\r\n */\r\nexport async function injectContent(\r\n distPath: string,\r\n options: {\r\n contentPath?: string\r\n seoPath?: string\r\n indexFile?: string\r\n } = {}\r\n): Promise<void> {\r\n const {\r\n contentPath = '.dcs/content.yaml',\r\n seoPath = '.dcs/seo.yaml',\r\n indexFile = 'index.html',\r\n } = options\r\n\r\n const indexPath = path.join(distPath, indexFile)\r\n\r\n // Check if index.html exists\r\n if (!fs.existsSync(indexPath)) {\r\n throw new Error(`index.html not found at ${indexPath}`)\r\n }\r\n\r\n // Try to load content.yaml\r\n let contentJson = 'undefined'\r\n try {\r\n const content = await loadContentYaml(contentPath)\r\n contentJson = JSON.stringify(content)\r\n console.log(`[dcs-inject] Loaded content from ${contentPath}`)\r\n } catch {\r\n console.log(`[dcs-inject] No content.yaml found at ${contentPath}`)\r\n }\r\n\r\n // Try to load seo.yaml\r\n let seoJson = 'undefined'\r\n try {\r\n const seo = await loadSeoYaml(seoPath)\r\n seoJson = JSON.stringify(seo)\r\n console.log(`[dcs-inject] Loaded SEO config from ${seoPath}`)\r\n } catch {\r\n console.log(`[dcs-inject] No seo.yaml found at ${seoPath}`)\r\n }\r\n\r\n // Read index.html\r\n let html = fs.readFileSync(indexPath, 'utf8')\r\n\r\n // Create injection script\r\n const injection = `\r\n<script>\r\n window.__DCS_CONTENT__ = ${contentJson};\r\n window.__DCS_SEO__ = ${seoJson};\r\n</script>\r\n`\r\n\r\n // Inject before </head>\r\n if (html.includes('</head>')) {\r\n html = html.replace('</head>', `${injection}</head>`)\r\n } else {\r\n // Fallback: inject at the start of <body>\r\n html = html.replace('<body', `${injection}<body`)\r\n }\r\n\r\n // Write modified index.html\r\n fs.writeFileSync(indexPath, html)\r\n console.log(`[dcs-inject] Content injected into ${indexPath}`)\r\n}\r\n\r\n// CLI entry point\r\nif (process.argv[1]?.includes('inject-content')) {\r\n const distPath = process.argv[2]\r\n if (!distPath) {\r\n console.error('Usage: dcs-inject <dist-path>')\r\n console.error('Example: dcs-inject dist/my-app')\r\n process.exit(1)\r\n }\r\n\r\n injectContent(distPath).catch((err) => {\r\n console.error('[dcs-inject] Error:', err.message)\r\n process.exit(1)\r\n })\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/scripts/inject-content.ts"],"names":[],"mappings":";;;;;AAwBA,eAAsB,aAAA,CACpB,QAAA,EACA,OAAA,GAII,EAAC,EACU;AACf,EAAA,MAAM;AAAA,IACJ,WAAA,GAAc,mBAAA;AAAA,IACd,OAAA,GAAU,eAAA;AAAA,IACV,SAAA,GAAY;AAAA,GACd,GAAI,OAAA;AAEJ,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,SAAS,CAAA;AAG/C,EAAA,IAAI,CAAC,EAAA,CAAG,UAAA,CAAW,SAAS,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,SAAS,CAAA,CAAE,CAAA;AAAA,EACxD;AAGA,EAAA,IAAI,WAAA,GAAc,WAAA;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,OAAA,GAAU,MAAM,eAAA,CAAgB,WAAW,CAAA;AACjD,IAAA,WAAA,GAAc,IAAA,CAAK,UAAU,OAAO,CAAA;AACpC,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iCAAA,EAAoC,WAAW,CAAA,CAAE,CAAA;AAAA,EAC/D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,sCAAA,EAAyC,WAAW,CAAA,CAAE,CAAA;AAAA,EACpE;AAGA,EAAA,IAAI,OAAA,GAAU,WAAA;AACd,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,WAAA,CAAY,OAAO,CAAA;AACrC,IAAA,OAAA,GAAU,IAAA,CAAK,UAAU,GAAG,CAAA;AAC5B,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,oCAAA,EAAuC,OAAO,CAAA,CAAE,CAAA;AAAA,EAC9D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,kCAAA,EAAqC,OAAO,CAAA,CAAE,CAAA;AAAA,EAC5D;AAGA,EAAA,IAAI,IAAA,GAAO,EAAA,CAAG,YAAA,CAAa,SAAA,EAAW,MAAM,CAAA;AAG5C,EAAA,MAAM,SAAA,GAAY;AAAA;AAAA,2BAAA,EAES,WAAW,CAAA;AAAA,uBAAA,EACf,OAAO,CAAA;AAAA;AAAA,CAAA;AAK9B,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,EAAG;AAC5B,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,CAAA,EAAG,SAAS,CAAA,OAAA,CAAS,CAAA;AAAA,EACtD,CAAA,MAAO;AAEL,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,KAAA,CAAO,CAAA;AAAA,EAClD;AAGA,EAAA,EAAA,CAAG,aAAA,CAAc,WAAW,IAAI,CAAA;AAChC,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,mCAAA,EAAsC,SAAS,CAAA,CAAE,CAAA;AAC/D;AAGA,IAAI,QAAQ,IAAA,CAAK,CAAC,CAAA,EAAG,QAAA,CAAS,gBAAgB,CAAA,EAAG;AAC/C,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AAC/B,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAA,CAAQ,MAAM,+BAA+B,CAAA;AAC7C,IAAA,OAAA,CAAQ,MAAM,iCAAiC,CAAA;AAC/C,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,aAAA,CAAc,QAAQ,CAAA,CAAE,KAAA,CAAM,CAAC,GAAA,KAAQ;AACrC,IAAA,OAAA,CAAQ,KAAA,CAAM,qBAAA,EAAuB,GAAA,CAAI,OAAO,CAAA;AAChD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB,CAAC,CAAA;AACH","file":"inject-content.js","sourcesContent":["#!/usr/bin/env node\n/**\n * Post-build script to inject DCS content into Angular's index.html\n *\n * Usage:\n * npx dcs-inject dist/my-app\n * node node_modules/@duffcloudservices/cms-angular/dist/scripts/inject-content.js dist/my-app\n *\n * This script:\n * 1. Reads .dcs/content.yaml and .dcs/seo.yaml from the project root\n * 2. Injects them as __DCS_CONTENT__ and __DCS_SEO__ globals in index.html\n * 3. Writes the modified index.html back to the dist folder\n */\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { loadContentYaml, loadSeoYaml } from '@duffcloudservices/cms-core'\n\n/**\n * Inject DCS content into an Angular build's index.html\n *\n * @param distPath - Path to the Angular build output (e.g., 'dist/my-app')\n * @param options - Optional configuration\n */\nexport async function injectContent(\n distPath: string,\n options: {\n contentPath?: string\n seoPath?: string\n indexFile?: string\n } = {}\n): Promise<void> {\n const {\n contentPath = '.dcs/content.yaml',\n seoPath = '.dcs/seo.yaml',\n indexFile = 'index.html',\n } = options\n\n const indexPath = path.join(distPath, indexFile)\n\n // Check if index.html exists\n if (!fs.existsSync(indexPath)) {\n throw new Error(`index.html not found at ${indexPath}`)\n }\n\n // Try to load content.yaml\n let contentJson = 'undefined'\n try {\n const content = await loadContentYaml(contentPath)\n contentJson = JSON.stringify(content)\n console.log(`[dcs-inject] Loaded content from ${contentPath}`)\n } catch {\n console.log(`[dcs-inject] No content.yaml found at ${contentPath}`)\n }\n\n // Try to load seo.yaml\n let seoJson = 'undefined'\n try {\n const seo = await loadSeoYaml(seoPath)\n seoJson = JSON.stringify(seo)\n console.log(`[dcs-inject] Loaded SEO config from ${seoPath}`)\n } catch {\n console.log(`[dcs-inject] No seo.yaml found at ${seoPath}`)\n }\n\n // Read index.html\n let html = fs.readFileSync(indexPath, 'utf8')\n\n // Create injection script\n const injection = `\n<script>\n window.__DCS_CONTENT__ = ${contentJson};\n window.__DCS_SEO__ = ${seoJson};\n</script>\n`\n\n // Inject before </head>\n if (html.includes('</head>')) {\n html = html.replace('</head>', `${injection}</head>`)\n } else {\n // Fallback: inject at the start of <body>\n html = html.replace('<body', `${injection}<body`)\n }\n\n // Write modified index.html\n fs.writeFileSync(indexPath, html)\n console.log(`[dcs-inject] Content injected into ${indexPath}`)\n}\n\n// CLI entry point\nif (process.argv[1]?.includes('inject-content')) {\n const distPath = process.argv[2]\n if (!distPath) {\n console.error('Usage: dcs-inject <dist-path>')\n console.error('Example: dcs-inject dist/my-app')\n process.exit(1)\n }\n\n injectContent(distPath).catch((err) => {\n console.error('[dcs-inject] Error:', err.message)\n process.exit(1)\n })\n}\n"]}
package/package.json CHANGED
@@ -1,67 +1,66 @@
1
- {
2
- "name": "@duffcloudservices/cms-angular",
3
- "version": "0.1.0",
4
- "description": "Angular services and build scripts for DCS CMS integration",
5
- "type": "module",
6
- "exports": {
7
- ".": {
8
- "types": "./dist/index.d.ts",
9
- "import": "./dist/index.js"
10
- },
11
- "./scripts": {
12
- "types": "./dist/scripts/index.d.ts",
13
- "import": "./dist/scripts/index.js"
14
- }
15
- },
16
- "main": "./dist/index.js",
17
- "types": "./dist/index.d.ts",
18
- "bin": {
19
- "dcs-inject": "./dist/scripts/inject-content.js"
20
- },
21
- "files": [
22
- "dist"
23
- ],
24
- "scripts": {
25
- "build": "tsup",
26
- "dev": "tsup --watch",
27
- "test": "vitest run",
28
- "test:watch": "vitest",
29
- "type-check": "tsc --noEmit",
30
- "prepublishOnly": "pnpm run build"
31
- },
32
- "peerDependencies": {
33
- "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0",
34
- "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0",
35
- "@angular/platform-browser": "^17.0.0 || ^18.0.0 || ^19.0.0"
36
- },
37
- "dependencies": {
38
- "@duffcloudservices/cms-core": "workspace:*"
39
- },
40
- "devDependencies": {
41
- "@angular/common": "^19.0.0",
42
- "@angular/core": "^19.0.0",
43
- "@angular/platform-browser": "^19.0.0",
44
- "@types/node": "^20.11.0",
45
- "rxjs": "^7.8.0",
46
- "tsup": "^8.0.0",
47
- "typescript": "~5.6.3",
48
- "vitest": "^3.2.3"
49
- },
50
- "keywords": [
51
- "angular",
52
- "dcs",
53
- "cms",
54
- "content",
55
- "seo"
56
- ],
57
- "author": "Duff Cloud Services",
58
- "license": "MIT",
59
- "repository": {
60
- "type": "git",
61
- "url": "https://github.com/duffn84/dcs-again",
62
- "directory": "packages/cms-angular"
63
- },
64
- "publishConfig": {
65
- "access": "public"
66
- }
67
- }
1
+ {
2
+ "name": "@duffcloudservices/cms-angular",
3
+ "version": "0.1.2",
4
+ "description": "Angular services and build scripts for DCS CMS integration",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./scripts": {
12
+ "types": "./dist/scripts/index.d.ts",
13
+ "import": "./dist/scripts/index.js"
14
+ }
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "bin": {
19
+ "dcs-inject": "./dist/scripts/inject-content.js"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "peerDependencies": {
25
+ "@angular/common": "^17.0.0 || ^18.0.0 || ^19.0.0",
26
+ "@angular/core": "^17.0.0 || ^18.0.0 || ^19.0.0",
27
+ "@angular/platform-browser": "^17.0.0 || ^18.0.0 || ^19.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@duffcloudservices/cms-core": "0.4.4"
31
+ },
32
+ "devDependencies": {
33
+ "@angular/common": "^19.0.0",
34
+ "@angular/core": "^19.0.0",
35
+ "@angular/platform-browser": "^19.0.0",
36
+ "@types/node": "^20.11.0",
37
+ "rxjs": "^7.8.0",
38
+ "tsup": "^8.0.0",
39
+ "typescript": "~5.6.3",
40
+ "vitest": "^3.2.3"
41
+ },
42
+ "keywords": [
43
+ "angular",
44
+ "dcs",
45
+ "cms",
46
+ "content",
47
+ "seo"
48
+ ],
49
+ "author": "Duff Cloud Services",
50
+ "license": "MIT",
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/duffn84/dcs-again",
54
+ "directory": "packages/cms-angular"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "scripts": {
60
+ "build": "tsup",
61
+ "dev": "tsup --watch",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest",
64
+ "type-check": "tsc --noEmit"
65
+ }
66
+ }