@pixelated-tech/components 3.13.15 → 3.14.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.
Files changed (247) hide show
  1. package/dist/components/admin/site-health/google.api.integration.js +5 -4
  2. package/dist/components/admin/site-health/site-health-cloudwatch.integration.js +3 -2
  3. package/dist/components/config/crypto.js +17 -1
  4. package/dist/components/general/cache-manager.js +19 -2
  5. package/dist/components/general/carousel.drag.js +21 -24
  6. package/dist/components/general/intersection-observer.js +4 -0
  7. package/dist/components/general/metadata.functions.js +1 -1
  8. package/dist/components/general/sitemap.js +3 -2
  9. package/dist/components/general/utilities.js +106 -0
  10. package/dist/components/integrations/contentful.delivery.js +16 -16
  11. package/dist/components/integrations/contentful.items.components.js +2 -11
  12. package/dist/components/integrations/flickr.js +7 -4
  13. package/dist/components/integrations/googleplaces.js +144 -0
  14. package/dist/components/integrations/socialcard.js +5 -2
  15. package/dist/components/integrations/wordpress.components.js +2 -1
  16. package/dist/components/shoppingcart/ebay.components.js +5 -5
  17. package/dist/components/shoppingcart/ebay.functions.js +5 -3
  18. package/dist/components/shoppingcart/shipping.to.json +3 -4
  19. package/dist/components/shoppingcart/shoppingcart.components.js +7 -5
  20. package/dist/components/shoppingcart/shoppingcart.css +1 -1
  21. package/dist/components/shoppingcart/shoppingcart.functions.js +5 -14
  22. package/dist/components/sitebuilder/form/formcomponents.js +151 -0
  23. package/dist/components/sitebuilder/form/formutils.js +3 -0
  24. package/dist/components/sitebuilder/page/lib/pageStorageContentful.js +2 -2
  25. package/dist/config/pixelated.config.json.enc +1 -1
  26. package/dist/data/form.json +18 -0
  27. package/dist/index.adminserver.js +1 -3
  28. package/dist/index.js +1 -1
  29. package/dist/index.server.js +1 -0
  30. package/dist/scripts/create-pixelated-app.js +187 -79
  31. package/dist/scripts/create-pixelated-app.json +51 -1
  32. package/dist/scripts/pixelated-eslint-plugin.js +142 -0
  33. package/dist/scripts/release.sh +23 -16
  34. package/dist/scripts/update.sh +47 -8
  35. package/dist/types/components/admin/deploy/deployment.integration.d.ts +1 -0
  36. package/dist/types/components/admin/deploy/deployment.integration.d.ts.map +1 -1
  37. package/dist/types/components/admin/site-health/google.api.integration.d.ts.map +1 -1
  38. package/dist/types/components/admin/site-health/site-health-cloudwatch.integration.d.ts.map +1 -1
  39. package/dist/types/components/config/config.types.d.ts +23 -0
  40. package/dist/types/components/config/config.types.d.ts.map +1 -1
  41. package/dist/types/components/config/crypto.d.ts +1 -0
  42. package/dist/types/components/config/crypto.d.ts.map +1 -1
  43. package/dist/types/components/general/cache-manager.d.ts +16 -2
  44. package/dist/types/components/general/cache-manager.d.ts.map +1 -1
  45. package/dist/types/components/general/carousel.drag.d.ts.map +1 -1
  46. package/dist/types/components/general/intersection-observer.d.ts.map +1 -1
  47. package/dist/types/components/general/sitemap.d.ts.map +1 -1
  48. package/dist/types/components/general/utilities.d.ts +39 -0
  49. package/dist/types/components/general/utilities.d.ts.map +1 -1
  50. package/dist/types/components/integrations/contentful.delivery.d.ts +16 -16
  51. package/dist/types/components/integrations/contentful.items.components.d.ts.map +1 -1
  52. package/dist/types/components/integrations/flickr.d.ts.map +1 -1
  53. package/dist/types/components/integrations/googleplaces.d.ts +61 -0
  54. package/dist/types/components/integrations/googleplaces.d.ts.map +1 -0
  55. package/dist/types/components/integrations/socialcard.d.ts.map +1 -1
  56. package/dist/types/components/integrations/wordpress.components.d.ts.map +1 -1
  57. package/dist/types/components/shoppingcart/ebay.functions.d.ts.map +1 -1
  58. package/dist/types/components/shoppingcart/shoppingcart.components.d.ts.map +1 -1
  59. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts +2 -2
  60. package/dist/types/components/shoppingcart/shoppingcart.functions.d.ts.map +1 -1
  61. package/dist/types/components/sitebuilder/form/formcomponents.d.ts +22 -0
  62. package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -1
  63. package/dist/types/components/sitebuilder/form/formutils.d.ts.map +1 -1
  64. package/dist/types/index.adminserver.d.ts +1 -3
  65. package/dist/types/index.d.ts +1 -1
  66. package/dist/types/index.server.d.ts +1 -0
  67. package/dist/types/scripts/create-pixelated-app.d.ts +3 -0
  68. package/dist/types/scripts/create-pixelated-app.d.ts.map +1 -1
  69. package/dist/types/scripts/pixelated-eslint-plugin.d.ts +20 -0
  70. package/dist/types/stories/integrations/contentful.items.stories.d.ts.map +1 -0
  71. package/dist/types/stories/integrations/contentful.stories.d.ts.map +1 -0
  72. package/dist/types/stories/integrations/google.reviews.stories.d.ts.map +1 -0
  73. package/dist/types/stories/integrations/googlesearch.stories.d.ts.map +1 -0
  74. package/dist/types/stories/integrations/gravatar.stories.d.ts.map +1 -0
  75. package/dist/types/stories/integrations/instagram.stories.d.ts.map +1 -0
  76. package/dist/types/stories/integrations/wordpress.stories.d.ts.map +1 -0
  77. package/dist/types/test/test-utils.d.ts +2 -0
  78. package/dist/types/test/test-utils.d.ts.map +1 -1
  79. package/dist/types/tests/404.test.d.ts +2 -0
  80. package/dist/types/tests/404.test.d.ts.map +1 -0
  81. package/dist/types/tests/carousel.drag.test.d.ts +2 -0
  82. package/dist/types/tests/carousel.drag.test.d.ts.map +1 -0
  83. package/dist/types/tests/carouselDrag.test.d.ts +2 -0
  84. package/dist/types/tests/carouselDrag.test.d.ts.map +1 -0
  85. package/dist/types/tests/componentAnalysis.test.d.ts +2 -0
  86. package/dist/types/tests/componentAnalysis.test.d.ts.map +1 -0
  87. package/dist/types/tests/componentDiscovery.test.d.ts +2 -0
  88. package/dist/types/tests/componentDiscovery.test.d.ts.map +1 -0
  89. package/dist/types/tests/componentMap.test.d.ts +2 -0
  90. package/dist/types/tests/componentMap.test.d.ts.map +1 -0
  91. package/dist/types/tests/contentful.items.components.test.d.ts +2 -0
  92. package/dist/types/tests/contentful.items.components.test.d.ts.map +1 -0
  93. package/dist/types/tests/contentful.management.test.d.ts +2 -0
  94. package/dist/types/tests/contentful.management.test.d.ts.map +1 -0
  95. package/dist/types/tests/contentfulManagement.test.d.ts +2 -0
  96. package/dist/types/tests/contentfulManagement.test.d.ts.map +1 -0
  97. package/dist/types/tests/countup.test.d.ts +2 -0
  98. package/dist/types/tests/countup.test.d.ts.map +1 -0
  99. package/dist/types/tests/crypto.test.d.ts +2 -0
  100. package/dist/types/tests/crypto.test.d.ts.map +1 -0
  101. package/dist/types/tests/deployment.integration.test.d.ts +2 -0
  102. package/dist/types/tests/deployment.integration.test.d.ts.map +1 -0
  103. package/dist/types/tests/ebay.components.test.d.ts +2 -0
  104. package/dist/types/tests/ebay.components.test.d.ts.map +1 -0
  105. package/dist/types/tests/ebayComponents.test.d.ts +2 -0
  106. package/dist/types/tests/ebayComponents.test.d.ts.map +1 -0
  107. package/dist/types/tests/flickr.test.d.ts +2 -0
  108. package/dist/types/tests/flickr.test.d.ts.map +1 -0
  109. package/dist/types/tests/formgoogleplacesinput.test.d.ts +2 -0
  110. package/dist/types/tests/formgoogleplacesinput.test.d.ts.map +1 -0
  111. package/dist/types/tests/formutils.test.d.ts +2 -0
  112. package/dist/types/tests/formutils.test.d.ts.map +1 -0
  113. package/dist/types/tests/formvalidator.test.d.ts +2 -0
  114. package/dist/types/tests/formvalidator.test.d.ts.map +1 -0
  115. package/dist/types/tests/gemini-api.client.test.d.ts +2 -0
  116. package/dist/types/tests/gemini-api.client.test.d.ts.map +1 -0
  117. package/dist/types/tests/gemini-api.server.test.d.ts +2 -0
  118. package/dist/types/tests/gemini-api.server.test.d.ts.map +1 -0
  119. package/dist/types/tests/geminiApi.test.d.ts +2 -0
  120. package/dist/types/tests/geminiApi.test.d.ts.map +1 -0
  121. package/dist/types/tests/google.reviews.components.test.d.ts +2 -0
  122. package/dist/types/tests/google.reviews.components.test.d.ts.map +1 -0
  123. package/dist/types/tests/googleanalytics.test.d.ts +2 -0
  124. package/dist/types/tests/googleanalytics.test.d.ts.map +1 -0
  125. package/dist/types/tests/googlemap.test.d.ts +2 -0
  126. package/dist/types/tests/googlemap.test.d.ts.map +1 -0
  127. package/dist/types/tests/gravatar.functions.test.d.ts +2 -0
  128. package/dist/types/tests/gravatar.functions.test.d.ts.map +1 -0
  129. package/dist/types/tests/hubspot.components.test.d.ts +2 -0
  130. package/dist/types/tests/hubspot.components.test.d.ts.map +1 -0
  131. package/dist/types/tests/image-utils.test.d.ts +2 -0
  132. package/dist/types/tests/image-utils.test.d.ts.map +1 -0
  133. package/dist/types/tests/instagram.components.test.d.ts +2 -0
  134. package/dist/types/tests/instagram.components.test.d.ts.map +1 -0
  135. package/dist/types/tests/instagram.functions.test.d.ts +2 -0
  136. package/dist/types/tests/instagram.functions.test.d.ts.map +1 -0
  137. package/dist/types/tests/intersection-observer.test.d.ts +2 -0
  138. package/dist/types/tests/intersection-observer.test.d.ts.map +1 -0
  139. package/dist/types/tests/metadata.functions.test.d.ts +2 -0
  140. package/dist/types/tests/metadata.functions.test.d.ts.map +1 -0
  141. package/dist/types/tests/metadataComponents.test.d.ts +2 -0
  142. package/dist/types/tests/metadataComponents.test.d.ts.map +1 -0
  143. package/dist/types/tests/page-storage.test.d.ts +2 -0
  144. package/dist/types/tests/page-storage.test.d.ts.map +1 -0
  145. package/dist/types/tests/pageStorageContentful.test.d.ts +2 -0
  146. package/dist/types/tests/pageStorageContentful.test.d.ts.map +1 -0
  147. package/dist/types/tests/pageStorageLocal.test.d.ts +2 -0
  148. package/dist/types/tests/pageStorageLocal.test.d.ts.map +1 -0
  149. package/dist/types/tests/pixelated.test.d.ts +2 -0
  150. package/dist/types/tests/pixelated.test.d.ts.map +1 -0
  151. package/dist/types/tests/propTypeIntrospection.test.d.ts +2 -0
  152. package/dist/types/tests/propTypeIntrospection.test.d.ts.map +1 -0
  153. package/dist/types/tests/save-route-example.test.d.ts +2 -0
  154. package/dist/types/tests/save-route-example.test.d.ts.map +1 -0
  155. package/dist/types/tests/saveRouteExample.test.d.ts +2 -0
  156. package/dist/types/tests/saveRouteExample.test.d.ts.map +1 -0
  157. package/dist/types/tests/seoConstants.test.d.ts +2 -0
  158. package/dist/types/tests/seoConstants.test.d.ts.map +1 -0
  159. package/dist/types/tests/site-health-accessibility.test.d.ts +2 -0
  160. package/dist/types/tests/site-health-accessibility.test.d.ts.map +1 -0
  161. package/dist/types/tests/site-health-cloudwatch.integration.test.d.ts +2 -0
  162. package/dist/types/tests/site-health-cloudwatch.integration.test.d.ts.map +1 -0
  163. package/dist/types/tests/site-health-dependency-vulnerabilities.test.d.ts +2 -0
  164. package/dist/types/tests/site-health-dependency-vulnerabilities.test.d.ts.map +1 -0
  165. package/dist/types/tests/site-health-github.test.d.ts +2 -0
  166. package/dist/types/tests/site-health-github.test.d.ts.map +1 -0
  167. package/dist/types/tests/site-health-google-analytics.integration.test.d.ts +2 -0
  168. package/dist/types/tests/site-health-google-analytics.integration.test.d.ts.map +1 -0
  169. package/dist/types/tests/site-health-google-analytics.test.d.ts +2 -0
  170. package/dist/types/tests/site-health-google-analytics.test.d.ts.map +1 -0
  171. package/dist/types/tests/site-health-google-search-console.integration.test.d.ts +2 -0
  172. package/dist/types/tests/site-health-google-search-console.integration.test.d.ts.map +1 -0
  173. package/dist/types/tests/site-health-google-search-console.test.d.ts +2 -0
  174. package/dist/types/tests/site-health-google-search-console.test.d.ts.map +1 -0
  175. package/dist/types/tests/site-health-mock-context.test.d.ts +2 -0
  176. package/dist/types/tests/site-health-mock-context.test.d.ts.map +1 -0
  177. package/dist/types/tests/site-health-on-site-seo.test.d.ts +2 -0
  178. package/dist/types/tests/site-health-on-site-seo.test.d.ts.map +1 -0
  179. package/dist/types/tests/site-health-performance.test.d.ts +2 -0
  180. package/dist/types/tests/site-health-performance.test.d.ts.map +1 -0
  181. package/dist/types/tests/site-health-security.integration.test.d.ts +2 -0
  182. package/dist/types/tests/site-health-security.integration.test.d.ts.map +1 -0
  183. package/dist/types/tests/site-health-security.test.d.ts +2 -0
  184. package/dist/types/tests/site-health-security.test.d.ts.map +1 -0
  185. package/dist/types/tests/site-health-seo.test.d.ts +2 -0
  186. package/dist/types/tests/site-health-seo.test.d.ts.map +1 -0
  187. package/dist/types/tests/site-health-uptime.integration.test.d.ts +2 -0
  188. package/dist/types/tests/site-health-uptime.integration.test.d.ts.map +1 -0
  189. package/dist/types/tests/site-health-uptime.test.d.ts +2 -0
  190. package/dist/types/tests/site-health-uptime.test.d.ts.map +1 -0
  191. package/dist/types/tests/siteHealthGaIntegration.test.d.ts +2 -0
  192. package/dist/types/tests/siteHealthGaIntegration.test.d.ts.map +1 -0
  193. package/dist/types/tests/siteHealthGscIntegration.test.d.ts +2 -0
  194. package/dist/types/tests/siteHealthGscIntegration.test.d.ts.map +1 -0
  195. package/dist/types/tests/spotify.components.test.d.ts +2 -0
  196. package/dist/types/tests/spotify.components.test.d.ts.map +1 -0
  197. package/dist/types/tests/spotify.functions.test.d.ts +2 -0
  198. package/dist/types/tests/spotify.functions.test.d.ts.map +1 -0
  199. package/dist/types/tests/test-utils.d.ts +7 -0
  200. package/dist/types/tests/test-utils.d.ts.map +1 -0
  201. package/dist/types/tests/usePageBuilder.test.d.ts +2 -0
  202. package/dist/types/tests/usePageBuilder.test.d.ts.map +1 -0
  203. package/package.json +48 -41
  204. package/dist/components/admin/site-health/site-health-google-analytics.integration.js +0 -6
  205. package/dist/components/admin/site-health/site-health-google-search-console.integration.js +0 -6
  206. package/dist/components/general/proxy-csp-listener.js +0 -20
  207. package/dist/scripts/create-pixelated-app-template-mapper.js +0 -80
  208. package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts +0 -6
  209. package/dist/types/components/admin/site-health/site-health-google-analytics.integration.d.ts.map +0 -1
  210. package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts +0 -6
  211. package/dist/types/components/admin/site-health/site-health-google-search-console.integration.d.ts.map +0 -1
  212. package/dist/types/components/general/proxy-csp-listener.d.ts +0 -15
  213. package/dist/types/components/general/proxy-csp-listener.d.ts.map +0 -1
  214. package/dist/types/scripts/create-pixelated-app-template-mapper.d.ts +0 -5
  215. package/dist/types/scripts/create-pixelated-app-template-mapper.d.ts.map +0 -1
  216. package/dist/types/stories/general/callout.many.stories.d.ts +0 -7
  217. package/dist/types/stories/general/callout.many.stories.d.ts.map +0 -1
  218. package/dist/types/stories/general/contentful.item.stories.d.ts +0 -12
  219. package/dist/types/stories/general/contentful.item.stories.d.ts.map +0 -1
  220. package/dist/types/stories/general/contentful.items.stories.d.ts.map +0 -1
  221. package/dist/types/stories/general/contentful.stories.d.ts.map +0 -1
  222. package/dist/types/stories/general/global-error.stories.d.ts +0 -26
  223. package/dist/types/stories/general/global-error.stories.d.ts.map +0 -1
  224. package/dist/types/stories/general/google.reviews.stories.d.ts.map +0 -1
  225. package/dist/types/stories/general/googleanalytics.stories.d.ts +0 -14
  226. package/dist/types/stories/general/googleanalytics.stories.d.ts.map +0 -1
  227. package/dist/types/stories/general/googlesearch.stories.d.ts.map +0 -1
  228. package/dist/types/stories/general/gravatar.stories.d.ts.map +0 -1
  229. package/dist/types/stories/general/instagram.stories.d.ts.map +0 -1
  230. package/dist/types/stories/general/loading.stories.d.ts +0 -11
  231. package/dist/types/stories/general/loading.stories.d.ts.map +0 -1
  232. package/dist/types/stories/general/metadata.stories.d.ts +0 -25
  233. package/dist/types/stories/general/metadata.stories.d.ts.map +0 -1
  234. package/dist/types/stories/general/schema.stories.d.ts +0 -62
  235. package/dist/types/stories/general/schema.stories.d.ts.map +0 -1
  236. package/dist/types/stories/general/sitemap.stories.d.ts +0 -8
  237. package/dist/types/stories/general/sitemap.stories.d.ts.map +0 -1
  238. package/dist/types/stories/general/wordpress.stories.d.ts.map +0 -1
  239. package/dist/types/stories/integrations/schema-podcast.stories.d.ts +0 -45
  240. package/dist/types/stories/integrations/schema-podcast.stories.d.ts.map +0 -1
  241. /package/dist/types/stories/{general → integrations}/contentful.items.stories.d.ts +0 -0
  242. /package/dist/types/stories/{general → integrations}/contentful.stories.d.ts +0 -0
  243. /package/dist/types/stories/{general → integrations}/google.reviews.stories.d.ts +0 -0
  244. /package/dist/types/stories/{general → integrations}/googlesearch.stories.d.ts +0 -0
  245. /package/dist/types/stories/{general → integrations}/gravatar.stories.d.ts +0 -0
  246. /package/dist/types/stories/{general → integrations}/instagram.stories.d.ts +0 -0
  247. /package/dist/types/stories/{general → integrations}/wordpress.stories.d.ts +0 -0
@@ -49,7 +49,6 @@ import { promisify } from 'util';
49
49
  import readline from 'readline/promises';
50
50
  import { stdin as input, stdout as output } from 'process';
51
51
  import { fileURLToPath } from 'url';
52
- import { loadManifest, findTemplateForSlug, pruneTemplateDirs, printAvailableTemplates } from './create-pixelated-app-template-mapper.js';
53
52
  import { AmplifyClient, CreateAppCommand, CreateBranchCommand, UpdateAppCommand, UpdateBranchCommand } from '@aws-sdk/client-amplify';
54
53
 
55
54
  const __filename = fileURLToPath(import.meta.url);
@@ -70,6 +69,51 @@ async function exists(p) {
70
69
  }
71
70
  }
72
71
 
72
+ // Template manifest utilities
73
+ export async function loadManifest(baseDir = path.resolve(__dirname)) {
74
+ const manifestPath = path.resolve(baseDir, 'create-pixelated-app.json');
75
+ try {
76
+ if (await exists(manifestPath)) {
77
+ const txt = await fs.readFile(manifestPath, 'utf8');
78
+ return JSON.parse(txt);
79
+ }
80
+ } catch (e) {
81
+ // ignore parse/read errors
82
+ }
83
+ return null;
84
+ }
85
+
86
+ export function findTemplateForSlug(manifest, slug) {
87
+ if (!manifest || !Array.isArray(manifest.templates)) return null;
88
+ slug = (slug || '').toLowerCase();
89
+ for (const t of manifest.templates) {
90
+ // Skip templates that have action: "ignore" (metadata routes in Admin section)
91
+ if (t.action === 'ignore') continue;
92
+ if (!t.aliases || !Array.isArray(t.aliases)) continue;
93
+ for (let a of t.aliases) {
94
+ a = a.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
95
+ if (a === slug) return t;
96
+ }
97
+ // also fuzzy match (e.g., 'about-us' -> 'about')
98
+ for (let a of t.aliases) {
99
+ a = a.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
100
+ if (slug === a || slug.startsWith(a + '-') || slug.endsWith('-' + a) || slug.includes('-' + a + '-')) return t;
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
106
+ export function printAvailableTemplates(manifest) {
107
+ if (!manifest || !Array.isArray(manifest.templates) || manifest.templates.length === 0) return;
108
+ console.log('\nAvailable templates:');
109
+ for (const t of manifest.templates) {
110
+ // Skip templates in Admin section (marked with action: "ignore")
111
+ if (t.action === 'ignore') continue;
112
+ const aliases = Array.isArray(t.aliases) ? t.aliases.join(', ') : '';
113
+ console.log(` - ${t.name}${aliases ? ': ' + aliases : ''}`);
114
+ }
115
+ }
116
+
73
117
  async function countFiles(src) {
74
118
  let total = 0;
75
119
  async function walk(p) {
@@ -245,7 +289,8 @@ export async function createAndPushRemote(destPath, siteName, defaultOwner) {
245
289
  const tmpDir = path.join(destPath, '.px-scripts');
246
290
  const tmpFile = path.join(tmpDir, 'get_github_token.ts');
247
291
  await fs.mkdir(tmpDir, { recursive: true });
248
- const tmpContent = `import('./src/components/config/config').then(m => {
292
+ const configModulePath = path.resolve(destPath, 'src', 'components', 'config', 'config');
293
+ const tmpContent = `import('${configModulePath}').then(m => {
249
294
  const cfg = m.getFullPixelatedConfig();
250
295
  // Only print the github object (or null) as JSON to stdout
251
296
  console.log(JSON.stringify(cfg?.github || null));
@@ -550,121 +595,184 @@ async function main() {
550
595
  }
551
596
 
552
597
 
553
- // Pages prompt: show available templates and ask which pages to create (comma-separated)
554
- console.log(`\nStep ${stepNumber++}: Page Creation`);
598
+ // Pages selection: ask which pages user wants (from template or custom new pages)
599
+ console.log(`\nStep ${stepNumber++}: Page Selection`);
555
600
  console.log('================================================================================\n');
556
601
  if (manifest && Array.isArray(manifest.templates) && manifest.templates.length) {
557
602
  printAvailableTemplates(manifest);
558
603
  }
559
- const pagesInput = (await rl.question('Pages to create (comma-separated, e.g. about,contact) [leave blank to skip]: ')).trim();
560
- let pagesToCreate = [];
561
- let existingPages = ['humans.txt', 'styleguide'];
604
+ const pagesInput = (await rl.question('Pages you want (comma-separated, e.g. about,contact,custom-legal) [leave blank to skip]: ')).trim();
605
+ let wantedPages = [];
562
606
  if (pagesInput) {
563
607
  const raw = pagesInput.split(',').map(s => s.trim()).filter(Boolean);
564
- // sanitize and normalize
565
608
  const seen = new Set();
566
- for (const r of raw) {
567
- const lower = r.toLowerCase();
568
- if (lower === 'home' || lower === 'index') {
569
- existingPages.push('home');
570
- continue;
571
- }
572
- let slug = r.trim().toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
573
- if (!slug) continue;
574
- if (seen.has(slug)) continue;
609
+
610
+ // Normalize each input
611
+ for (const userInput of raw) {
612
+ const slug = userInput.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
613
+ if (!slug || seen.has(slug)) continue;
575
614
  seen.add(slug);
615
+
616
+ // Check if it's a template page
576
617
  const matchedTemplate = findTemplateForSlug(manifest, slug);
577
- pagesToCreate.push({ slug, displayName: r.trim(), template: matchedTemplate });
618
+ const isTemplate = !!matchedTemplate;
619
+
620
+ wantedPages.push({
621
+ slug,
622
+ displayName: userInput.trim(),
623
+ isTemplate,
624
+ matchedTemplate
625
+ });
578
626
  }
579
627
 
580
- console.log('\nSummary of pages:');
581
- if (existingPages.length) console.log(` - Existing (skipped): ${existingPages.join(', ')}`);
582
- if (pagesToCreate.length) {
583
- console.log(' - To be created:');
584
- for (const p of pagesToCreate) {
585
- if (p.template) {
586
- console.log(` - ${p.slug} (mapped to template: ${p.template.name})`);
587
- } else {
588
- console.log(` - ${p.slug} (no template match)`);
628
+ if (wantedPages.length) {
629
+ console.log('\nSummary of pages:');
630
+ const templatePages = wantedPages.filter(p => p.isTemplate);
631
+ const customPages = wantedPages.filter(p => !p.isTemplate);
632
+
633
+ if (templatePages.length) {
634
+ console.log(' - Template pages:');
635
+ for (const p of templatePages) {
636
+ console.log(` - ${p.slug} (from template: ${p.matchedTemplate.name})`);
637
+ }
638
+ }
639
+ if (customPages.length) {
640
+ console.log(' - Custom pages (new):');
641
+ for (const p of customPages) {
642
+ console.log(` - ${p.slug}`);
589
643
  }
590
644
  }
591
645
  }
592
- const proceedPages = (await rl.question('Proceed to create these pages? (Y/n): ')) || 'y';
646
+
647
+ const proceedPages = (await rl.question('Proceed with these pages? (Y/n): ')) || 'y';
593
648
  if (proceedPages.toLowerCase() === 'y' || proceedPages.toLowerCase() === 'yes') {
594
- // perform creation
595
- const templatePagesHome = path.join(templatePath, 'src', 'app', '(pages)', '(home)');
596
649
  const siteRoutesFile = path.join(destPath, 'src', 'app', 'data', 'routes.json');
597
650
  let routesJson = null;
651
+
598
652
  try {
599
653
  routesJson = JSON.parse(await fs.readFile(siteRoutesFile, 'utf8'));
600
- // Ensure siteInfo exists and set its name to the root display name
601
654
  routesJson.siteInfo = routesJson.siteInfo || {};
602
655
  routesJson.siteInfo.name = rootDisplayName;
603
656
  } catch (e) {
604
- console.warn('⚠️ Could not read routes.json, routes will not be updated.');
657
+ console.error(' Failed to read routes.json:', e?.message || e);
658
+ process.exit(1);
605
659
  }
606
660
 
607
- for (const p of pagesToCreate) {
608
- const targetDir = path.join(destPath, 'src', 'app', '(pages)', p.slug);
609
- console.log(`Creating page ${p.slug} -> ${targetDir}`);
610
- let copyResult = null;
611
- if (p.template && p.template.src) {
612
- copyResult = await copyTemplateForPage(templatePath, p.template.src, templatePagesHome, targetDir);
613
- if (copyResult.used === 'template') {
614
- console.log(` - Copied template ${p.template.name} from ${copyResult.src}`);
615
- } else {
616
- console.warn(`⚠️ Template source ${path.join(templatePath, 'src', 'app', '(pages)', path.basename(p.template.src))} not found; using default page template instead.`);
661
+ const templatePagesHome = path.join(templatePath, 'src', 'app', '(pages)', '(home)');
662
+ const pagesDir = path.join(destPath, 'src', 'app', '(pages)');
663
+ const wantedSlugs = new Set(wantedPages.map(p => p.slug));
664
+
665
+ // Process wanted pages: rename templates or create custom pages
666
+ for (const p of wantedPages) {
667
+ if (p.isTemplate) {
668
+ // Get the actual template folder name (e.g., "about")
669
+ const templateFolderName = path.basename(p.matchedTemplate.src);
670
+ const oldPath = path.join(pagesDir, templateFolderName);
671
+ const newPath = path.join(pagesDir, p.slug);
672
+
673
+ // Rename if user requested a different name
674
+ if (templateFolderName !== p.slug) {
675
+ try {
676
+ await fs.rename(oldPath, newPath);
677
+ console.log(`Renamed ${templateFolderName} → ${p.slug}`);
678
+
679
+ // Update component name in page.tsx
680
+ const pageFile = path.join(newPath, 'page.tsx');
681
+ let content = await fs.readFile(pageFile, 'utf8');
682
+ const compName = p.displayName.replace(/[^a-zA-Z0-9]+/g, ' ').split(/\s+/).map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('') + 'Page';
683
+ content = content.replace(/export default function\s+\w+\s*\(/, `export default function ${compName}(`);
684
+ await fs.writeFile(pageFile, content, 'utf8');
685
+ console.log(` - Updated component name to ${compName}`);
686
+
687
+ // Update route path in routes.json
688
+ const route = routesJson.routes.find(r => r.path === `/${templateFolderName}`);
689
+ if (route) {
690
+ route.path = `/${p.slug}`;
691
+ route.name = p.displayName.split(/\s+/).map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' ');
692
+ console.log(` - Updated route path to /${p.slug}`);
693
+ }
694
+ } catch (e) {
695
+ console.error(`❌ Failed to rename ${templateFolderName} to ${p.slug}:`, e?.message || e);
696
+ process.exit(1);
697
+ }
617
698
  }
618
699
  } else {
619
- await copyRecursive(templatePagesHome, targetDir);
620
- }
621
- // rename component in page.tsx
622
- const pageFile = path.join(targetDir, 'page.tsx');
623
- try {
624
- let content = await fs.readFile(pageFile, 'utf8');
625
- const compName = p.displayName.replace(/[^a-zA-Z0-9]+/g,' ').split(/\s+/).map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join('') + 'Page';
626
- content = content.replace(/export default function\s+\w+\s*\(/, `export default function ${compName}(`);
627
- await fs.writeFile(pageFile, content, 'utf8');
628
- console.log(` - Updated component name to ${compName} in ${path.relative(destPath, pageFile)}`);
629
- } catch (e) {
630
- console.warn(`⚠️ Failed to update component name for ${p.slug}:`, e?.message || e);
631
- }
632
-
633
- // update routes.json
634
- if (routesJson && Array.isArray(routesJson.routes)) {
635
- // Skip if route path already exists
636
- const candidatePath = `/${p.slug}`;
637
- if (!routesJson.routes.some(r => r.path === candidatePath)) {
638
- routesJson.routes.push({
639
- "name": p.displayName.split(/\s+/).map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join(' '),
640
- "path": candidatePath,
700
+ // Create custom page from default template
701
+ try {
702
+ const targetDir = path.join(pagesDir, p.slug);
703
+ await copyRecursive(templatePagesHome, targetDir);
704
+ console.log(`Created custom page ${p.slug}`);
705
+
706
+ // Update component name in page.tsx
707
+ const pageFile = path.join(targetDir, 'page.tsx');
708
+ let content = await fs.readFile(pageFile, 'utf8');
709
+ const compName = p.displayName.replace(/[^a-zA-Z0-9]+/g, ' ').split(/\s+/).map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('') + 'Page';
710
+ content = content.replace(/export default function\s+\w+\s*\(/, `export default function ${compName}(`);
711
+ await fs.writeFile(pageFile, content, 'utf8');
712
+ console.log(` - Updated component name to ${compName}`);
713
+
714
+ // Add route entry
715
+ const newRoute = {
716
+ "name": p.displayName.split(/\s+/).map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' '),
717
+ "path": `/${p.slug}`,
641
718
  "title": `${rootDisplayName} - ${p.displayName}`,
642
719
  "description": "",
643
720
  "keywords": ""
644
- });
645
- } else {
646
- console.log(` - Route ${candidatePath} already exists; skipping route add.`);
721
+ };
722
+ routesJson.routes.push(newRoute);
723
+ console.log(` - Added route /${p.slug}`);
724
+ } catch (e) {
725
+ console.error(`❌ Failed to create custom page ${p.slug}:`, e?.message || e);
726
+ process.exit(1);
647
727
  }
648
728
  }
649
729
  }
650
730
 
651
- if (manifest) {
652
- const removed = await pruneTemplateDirs(manifest, destPath, pagesToCreate.map(p=>p.slug));
653
- for (const r of removed) {
654
- console.log(`Removed unused template page ${r} from new site...`);
731
+ // Delete unwanted template pages
732
+ // The Admin section (an object with nested routes for metadata pages) is in routesJson.routes
733
+ // but doesn't have folders in the (pages) directory, so it's never encountered by fs.readdir().
734
+ // We can safely delete page folders without worrying about removing the Admin section.
735
+ try {
736
+ const existingFolders = await fs.readdir(pagesDir);
737
+ const foldersToDelete = [];
738
+
739
+ for (const folderName of existingFolders) {
740
+ if (!wantedSlugs.has(folderName)) {
741
+ const folderTemplate = findTemplateForSlug(manifest, folderName);
742
+ if (folderTemplate) {
743
+ foldersToDelete.push(folderName);
744
+ }
745
+ }
746
+ }
747
+
748
+ // Delete unwanted folders and their routes
749
+ for (const folderName of foldersToDelete) {
750
+ try {
751
+ const folderPath = path.join(pagesDir, folderName);
752
+ await fs.rm(folderPath, { recursive: true });
753
+ console.log(`Deleted template page ${folderName}`);
754
+
755
+ // Remove the corresponding route from routes.json
756
+ routesJson.routes = routesJson.routes.filter(r => r.path !== `/${folderName}`);
757
+ } catch (e) {
758
+ console.warn(`⚠️ Failed to delete ${folderName}:`, e?.message || e);
759
+ }
655
760
  }
761
+ } catch (e) {
762
+ // readdir might fail if pages dir doesn't exist, but that's OK
763
+ console.warn(`⚠️ Could not read pages directory for cleanup:`, e?.message || e);
656
764
  }
657
765
 
658
- if (routesJson) {
659
- try {
660
- await fs.writeFile(siteRoutesFile, JSON.stringify(routesJson, null, '\t'), 'utf8');
661
- console.log('✅ routes.json updated.');
662
- } catch (e) {
663
- console.warn('⚠️ Failed to write routes.json:', e?.message || e);
664
- }
766
+ // Write final routes.json
767
+ try {
768
+ await fs.writeFile(siteRoutesFile, JSON.stringify(routesJson, null, '\t'), 'utf8');
769
+ console.log('✅ routes.json updated.');
770
+ } catch (e) {
771
+ console.error('Failed to write routes.json:', e?.message || e);
772
+ process.exit(1);
665
773
  }
666
774
  } else {
667
- console.log('Skipping page creation.');
775
+ console.log('Skipping page selection.');
668
776
  }
669
777
  }
670
778
 
@@ -10,6 +10,11 @@
10
10
  "aliases": ["blog", "blog-posts", "news", "updates", "articles"],
11
11
  "src": "../../pixelated-template/src/app/(pages)/blog"
12
12
  },
13
+ {
14
+ "name": "Blog Calendar",
15
+ "aliases": ["blogcalendar", "blog-calendar", "calendar", "event-calendar", "events"],
16
+ "src": "../../pixelated-template/src/app/(pages)/blogcalendar"
17
+ },
13
18
  {
14
19
  "name": "Contact",
15
20
  "aliases": ["contact", "contact-us", "contact-us-page", "contactus", "support", "get-in-touch", "reach-out", "get-in-touch"],
@@ -22,10 +27,40 @@
22
27
  "src": "../../pixelated-template/src/app/(pages)/faqs",
23
28
  "associated_files": ["src/app/data/faqs.json"]
24
29
  },
30
+ {
31
+ "name": "Global Error",
32
+ "aliases": ["global-error", "globalerror", "error-boundary"],
33
+ "action": "ignore"
34
+ },
25
35
  {
26
36
  "name": "Humans.txt",
27
37
  "aliases": ["humans", "humans-txt", "humans.txt", "humansfile", "humansfile.txt", "humanstext"],
28
- "src": "../../pixelated-template/src/app/(pages)/humans"
38
+ "action": "ignore"
39
+ },
40
+ {
41
+ "name": "Loading",
42
+ "aliases": ["loading", "skeleton", "loader"],
43
+ "action": "ignore"
44
+ },
45
+ {
46
+ "name": "Manifest",
47
+ "aliases": ["manifest", "manifest.json", "manifest-json", "webmanifest"],
48
+ "action": "ignore"
49
+ },
50
+ {
51
+ "name": "Not Found",
52
+ "aliases": ["not-found", "notfound", "404", "error-404"],
53
+ "action": "ignore"
54
+ },
55
+ {
56
+ "name": "Partners",
57
+ "aliases": ["partners", "partner", "affiliates", "integration-partners", "integrations", "collaborators"],
58
+ "src": "../../pixelated-template/src/app/(pages)/partners"
59
+ },
60
+ {
61
+ "name": "Podcast",
62
+ "aliases": ["podcast", "podcasts", "audio", "episodes", "show"],
63
+ "src": "../../pixelated-template/src/app/(pages)/podcast"
29
64
  },
30
65
  {
31
66
  "name": "Projects",
@@ -33,11 +68,26 @@
33
68
  "our-projects", "our-project", "case-studies", "examples", "showcase", "samples"],
34
69
  "src": "../../pixelated-template/src/app/(pages)/projects"
35
70
  },
71
+ {
72
+ "name": "Robots",
73
+ "aliases": ["robots", "robots.txt", "robots-txt"],
74
+ "action": "ignore"
75
+ },
76
+ {
77
+ "name": "Security.txt",
78
+ "aliases": ["security", "security.txt", "securitytxt", "security-txt", "well-known", "securityfile", "securityfile.txt", "wellknown"],
79
+ "action": "ignore"
80
+ },
36
81
  {
37
82
  "name": "Services",
38
83
  "aliases": ["services", "service", "our-services", "services-page", "offerings", "solutions", "products"],
39
84
  "src": "../../pixelated-template/src/app/(pages)/services"
40
85
  },
86
+ {
87
+ "name": "Sitemap",
88
+ "aliases": ["sitemap", "sitemap.xml", "sitemap-xml"],
89
+ "action": "ignore"
90
+ },
41
91
  {
42
92
  "name": "Style Guide",
43
93
  "aliases": ["styleguide", "style-guide", "design-system", "ui-kit", "pattern-library"],
@@ -1152,6 +1152,146 @@ const noDuplicateExportNamesRule = {
1152
1152
  }
1153
1153
  };
1154
1154
 
1155
+ /**
1156
+ * no-hardcoded-config-keys Rule
1157
+ *
1158
+ * Prevents hardcoding of Pixelated-specific configuration keys that should come from:
1159
+ * - Config provider (usePixelatedConfig)
1160
+ * - Environment variables
1161
+ * - Function parameters/props
1162
+ *
1163
+ * Only flags keys that are Pixelated-specific to avoid false positives.
1164
+ * Severity: ERROR for secrets, WARNING for other Pixelated config keys.
1165
+ */
1166
+ const noHardcodedConfigKeysRule = {
1167
+ meta: {
1168
+ type: 'problem',
1169
+ docs: {
1170
+ description: 'Prevent hardcoded Pixelated configuration keys that should come from config provider',
1171
+ category: 'Security',
1172
+ recommended: true,
1173
+ },
1174
+ fixable: false,
1175
+ schema: [],
1176
+ messages: {
1177
+ secretConfigKey: 'Hardcoded SECRET configuration key "{{keyName}}" ({{serviceName}}) detected. Must use config provider (usePixelatedConfig) or environment variables.',
1178
+ publicConfigKey: 'Hardcoded Pixelated config key "{{keyName}}" ({{serviceName}}) detected. Should use config provider (usePixelatedConfig) or pass as parameter instead.',
1179
+ },
1180
+ },
1181
+ create(context) {
1182
+ // Pixelated-SPECIFIC configuration keys (keys that are unlikely to be false positives)
1183
+ // Keys that are unique/distinctive to Pixelated integrations, excluding generic names
1184
+ const pixelatedConfigKeys = {
1185
+ // Contentful (very Pixelated-specific)
1186
+ contentful: ['space_id', 'delivery_access_token', 'management_access_token', 'preview_access_token', 'proxyURL', 'base_url', 'environment'],
1187
+ // eBay (very distinctive)
1188
+ ebay: ['appId', 'appDevId', 'appCertId', 'sbxAppId', 'sbxAppDevId', 'sbxAppCertId', 'globalId', 'baseTokenURL', 'baseSearchURL', 'baseAnalyticsURL', 'qsSearchURL', 'baseItemURL', 'qsItemURL'],
1189
+ // AWS (distinctive with service prefix or secretive names)
1190
+ aws: ['access_key_id', 'secret_access_key', 'session_token'],
1191
+ // Cloudinary
1192
+ cloudinary: ['product_env', 'api_key', 'api_secret'],
1193
+ // Flickr
1194
+ flickr: ['user_id'],
1195
+ // GitHub
1196
+ github: ['token', 'apiBaseUrl', 'defaultOwner'],
1197
+ // Google services
1198
+ google: ['client_id', 'client_secret', 'api_key', 'refresh_token'],
1199
+ // Google Analytics
1200
+ googleAnalytics: [], // Skip 'id' and 'adId' as too generic
1201
+ // Google Maps
1202
+ googleMaps: ['apiKey'],
1203
+ // HubSpot (keep only distinctive ones)
1204
+ hubspot: ['portalId', 'formId', 'trackingCode'],
1205
+ // Instagram
1206
+ instagram: ['accessToken', 'userId'],
1207
+ // NextAuth
1208
+ nextAuth: ['secret'],
1209
+ // PayPal
1210
+ paypal: ['sandboxPayPalApiKey', 'sandboxPayPalSecret', 'payPalApiKey', 'payPalSecret'],
1211
+ // WordPress
1212
+ wordpress: ['baseURL', 'site'],
1213
+ // Puppeteer
1214
+ puppeteer: ['executable_path', 'cache_dir'],
1215
+ // Global
1216
+ global: ['PIXELATED_CONFIG_KEY']
1217
+ };
1218
+
1219
+ // Secret keys that should NEVER be hardcoded (ERROR severity)
1220
+ const secretKeys = new Set([
1221
+ // AWS
1222
+ 'access_key_id', 'secret_access_key', 'session_token',
1223
+ // Cloudinary
1224
+ 'api_key', 'api_secret',
1225
+ // Contentful
1226
+ 'management_access_token', 'preview_access_token',
1227
+ // eBay
1228
+ 'sbxAppId',
1229
+ // GitHub
1230
+ 'token',
1231
+ // Instagram
1232
+ 'accessToken',
1233
+ // PayPal
1234
+ 'sandboxPayPalApiKey', 'sandboxPayPalSecret', 'payPalApiKey', 'payPalSecret',
1235
+ // NextAuth
1236
+ 'secret',
1237
+ // Global
1238
+ 'PIXELATED_CONFIG_KEY'
1239
+ ]);
1240
+
1241
+ // Build set of all known Pixelated config keys
1242
+ const allConfigKeys = new Set();
1243
+ Object.values(pixelatedConfigKeys).forEach(keys => {
1244
+ keys.forEach(key => allConfigKeys.add(key));
1245
+ });
1246
+
1247
+ function findServiceForKey(keyName) {
1248
+ for (const [serviceName, keys] of Object.entries(pixelatedConfigKeys)) {
1249
+ if (keys.includes(keyName)) {
1250
+ return serviceName;
1251
+ }
1252
+ }
1253
+ return 'unknown';
1254
+ }
1255
+
1256
+ function isConfigKey(keyName) {
1257
+ return allConfigKeys.has(keyName);
1258
+ }
1259
+
1260
+ function isSecretKey(keyName) {
1261
+ return secretKeys.has(keyName);
1262
+ }
1263
+
1264
+ return {
1265
+ ObjectExpression(node) {
1266
+ node.properties.forEach(prop => {
1267
+ // Check properties that look like { space_id: "value" } or { api_key: "..." }
1268
+ if (prop.type === 'Property' && prop.key) {
1269
+ const keyName = prop.key.name || prop.key.value;
1270
+
1271
+ // Check if this is a known Pixelated config key
1272
+ if (isConfigKey(keyName)) {
1273
+ // Check if value is a string literal (hardcoded)
1274
+ if (prop.value.type === 'Literal' && typeof prop.value.value === 'string') {
1275
+ const stringValue = prop.value.value;
1276
+ if (stringValue && stringValue.length > 0) {
1277
+ const serviceName = findServiceForKey(keyName);
1278
+ const messageId = isSecretKey(keyName) ? 'secretConfigKey' : 'publicConfigKey';
1279
+
1280
+ context.report({
1281
+ node: prop,
1282
+ messageId,
1283
+ data: { keyName, serviceName },
1284
+ });
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1289
+ });
1290
+ }
1291
+ };
1292
+ }
1293
+ };
1294
+
1155
1295
  export default {
1156
1296
  rules: {
1157
1297
  'prop-types-inferprops': propTypesInferPropsRule,
@@ -1169,6 +1309,7 @@ export default {
1169
1309
  'file-name-kebab-case': fileNameKebabCaseRule,
1170
1310
  'no-duplicate-export-names': noDuplicateExportNamesRule,
1171
1311
  'class-name-kebab-case': classNameKebabCaseRule,
1312
+ 'no-hardcoded-config-keys': noHardcodedConfigKeysRule,
1172
1313
  },
1173
1314
  configs: {
1174
1315
  recommended: {
@@ -1188,6 +1329,7 @@ export default {
1188
1329
  'pixelated/required-proptypes-jsdoc': 'error',
1189
1330
  'pixelated/no-duplicate-export-names': 'error',
1190
1331
  'pixelated/class-name-kebab-case': 'error',
1332
+ 'pixelated/no-hardcoded-config-keys': 'error',
1191
1333
  },
1192
1334
  },
1193
1335
  },
@@ -164,21 +164,15 @@ fi
164
164
  echo ""
165
165
  echo "📦 Step $((STEP_COUNT++)): Updating dependencies (all sections)..."
166
166
  echo "================================================="
167
- # iterate through prod/dev/optional sections, only bumping same-major versions
168
- for scope in "" dev optional; do
169
- flag=$([ "$scope" ] && echo "--$scope" || echo "")
170
- save=$([ "$scope" ] && echo "--save-$scope" || echo "--save")
171
- pkgs=$(npm outdated $flag --parseable --long | awk -F: '{ split($2,c,"@"); split($4,l,"@"); split(c[2],cv,"\\."); split(l[2],lv,"\\."); if(cv[1]==lv[1]) print $4 }')
172
- if [ -n "$pkgs" ]; then
173
- echo "Updating $scope packages: $pkgs"
174
- echo "$pkgs" | xargs npm install --force $save 2>/dev/null || true
175
- else
176
- echo "✅ No $scope updates needed"
177
- fi
178
- done
179
- # report peer deps separately
180
- peers=$(npm outdated --parseable --long --peer | awk -F: '{print $4}')
181
- printf "peer deps (manual): %s\n" "$peers"
167
+ if [ -f "src/scripts/update.sh" ]; then
168
+ bash src/scripts/update.sh
169
+ elif [ -f "scripts/update.sh" ]; then
170
+ bash scripts/update.sh
171
+ elif command -v npm &> /dev/null; then
172
+ npm run update 2>/dev/null || echo "⚠️ No update script found; skipping dependency update"
173
+ else
174
+ echo "⚠️ Could not find update.sh; skipping dependency update"
175
+ fi
182
176
 
183
177
 
184
178
 
@@ -198,7 +192,20 @@ npm run lint
198
192
 
199
193
 
200
194
  echo ""
201
- echo "🔨 Step $((STEP_COUNT++)): Encrypting configuration..."
195
+ echo " Step $((STEP_COUNT++)): Running code coverage check..."
196
+ echo "================================================="
197
+ # Check if test directory exists before running coverage
198
+ if [ -d "src/tests" ] || [ -d "src/test" ]; then
199
+ npm run test:coverage || exit 1
200
+ echo "✅ Coverage check passed."
201
+ else
202
+ echo "⚠️ No test directory found; skipping coverage check"
203
+ fi
204
+
205
+
206
+
207
+ echo ""
208
+ echo "�🔨 Step $((STEP_COUNT++)): Encrypting configuration..."
202
209
  echo "================================================="
203
210
  if grep -q "\"config:encrypt\":" package.json; then
204
211
  echo "🔒 Encrypting configuration..."