@mindful-web/marko-web 1.0.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 (367) hide show
  1. package/.eslintignore +3 -0
  2. package/LICENSE +21 -0
  3. package/browser/.eslintrc.js +1 -0
  4. package/browser/components/element-id.js +1 -0
  5. package/browser/components/gated-download/wufoo.vue +97 -0
  6. package/browser/components/image-slide.vue +54 -0
  7. package/browser/components/image-slider.vue +87 -0
  8. package/browser/components/index.js +15 -0
  9. package/browser/components/load-more-trigger.vue +129 -0
  10. package/browser/components/oembed.vue +133 -0
  11. package/browser/components/trigger-in-view-event.vue +41 -0
  12. package/browser/components/trigger-screen-change-event.vue +94 -0
  13. package/browser/event-bus.js +5 -0
  14. package/browser/index.js +83 -0
  15. package/browser/jquery-full.js +3 -0
  16. package/browser/jquery.js +3 -0
  17. package/browser/utils/clean-path.js +4 -0
  18. package/browser/vue.js +3 -0
  19. package/components/browser-component/script.marko +10 -0
  20. package/components/browser-component/script.marko.js +39 -0
  21. package/components/browser-component.marko +25 -0
  22. package/components/browser-component.marko.js +75 -0
  23. package/components/deferred-stylesheet.marko +6 -0
  24. package/components/deferred-stylesheet.marko.js +28 -0
  25. package/components/document/components/body-wrapper.marko +8 -0
  26. package/components/document/components/body-wrapper.marko.js +33 -0
  27. package/components/document/components/error.marko +26 -0
  28. package/components/document/components/error.marko.js +88 -0
  29. package/components/document/components/live-reload.marko +7 -0
  30. package/components/document/components/live-reload.marko.js +30 -0
  31. package/components/document/components/polyfill.marko +8 -0
  32. package/components/document/components/polyfill.marko.js +33 -0
  33. package/components/document/components/styles.marko +20 -0
  34. package/components/document/components/styles.marko.js +61 -0
  35. package/components/document/container.marko +15 -0
  36. package/components/document/container.marko.js +54 -0
  37. package/components/document/index.marko +63 -0
  38. package/components/document/index.marko.js +139 -0
  39. package/components/document/marko.json +46 -0
  40. package/components/element/array.marko +19 -0
  41. package/components/element/array.marko.js +67 -0
  42. package/components/element/audio.marko +5 -0
  43. package/components/element/audio.marko.js +33 -0
  44. package/components/element/block.marko +11 -0
  45. package/components/element/block.marko.js +44 -0
  46. package/components/element/clear.marko +4 -0
  47. package/components/element/clear.marko.js +32 -0
  48. package/components/element/components/image.marko +32 -0
  49. package/components/element/components/image.marko.js +62 -0
  50. package/components/element/components/text.marko +11 -0
  51. package/components/element/components/text.marko.js +37 -0
  52. package/components/element/content/address1.marko +14 -0
  53. package/components/element/content/address1.marko.js +40 -0
  54. package/components/element/content/address2.marko +14 -0
  55. package/components/element/content/address2.marko.js +40 -0
  56. package/components/element/content/audio.marko +13 -0
  57. package/components/element/content/audio.marko.js +39 -0
  58. package/components/element/content/authors.marko +12 -0
  59. package/components/element/content/authors.marko.js +43 -0
  60. package/components/element/content/body.marko +28 -0
  61. package/components/element/content/body.marko.js +66 -0
  62. package/components/element/content/byline.marko +15 -0
  63. package/components/element/content/byline.marko.js +41 -0
  64. package/components/element/content/city-state-zip.marko +14 -0
  65. package/components/element/content/city-state-zip.marko.js +40 -0
  66. package/components/element/content/contributors.marko +12 -0
  67. package/components/element/content/contributors.marko.js +43 -0
  68. package/components/element/content/country.marko +14 -0
  69. package/components/element/content/country.marko.js +40 -0
  70. package/components/element/content/embed-code.marko +15 -0
  71. package/components/element/content/embed-code.marko.js +41 -0
  72. package/components/element/content/end-date.marko +17 -0
  73. package/components/element/content/end-date.marko.js +43 -0
  74. package/components/element/content/ends.marko +14 -0
  75. package/components/element/content/ends.marko.js +40 -0
  76. package/components/element/content/fax.marko +14 -0
  77. package/components/element/content/fax.marko.js +40 -0
  78. package/components/element/content/images.marko +12 -0
  79. package/components/element/content/images.marko.js +43 -0
  80. package/components/element/content/marko.json +328 -0
  81. package/components/element/content/mobile.marko +14 -0
  82. package/components/element/content/mobile.marko.js +40 -0
  83. package/components/element/content/name.marko +15 -0
  84. package/components/element/content/name.marko.js +41 -0
  85. package/components/element/content/phone.marko +14 -0
  86. package/components/element/content/phone.marko.js +40 -0
  87. package/components/element/content/photographers.marko +12 -0
  88. package/components/element/content/photographers.marko.js +43 -0
  89. package/components/element/content/public-email.marko +19 -0
  90. package/components/element/content/public-email.marko.js +47 -0
  91. package/components/element/content/published.marko +17 -0
  92. package/components/element/content/published.marko.js +43 -0
  93. package/components/element/content/short-name.marko +15 -0
  94. package/components/element/content/short-name.marko.js +41 -0
  95. package/components/element/content/sidebar-stubs.marko +24 -0
  96. package/components/element/content/sidebar-stubs.marko.js +63 -0
  97. package/components/element/content/sidebars.marko +27 -0
  98. package/components/element/content/sidebars.marko.js +63 -0
  99. package/components/element/content/source.marko +14 -0
  100. package/components/element/content/source.marko.js +40 -0
  101. package/components/element/content/sponsors.marko +12 -0
  102. package/components/element/content/sponsors.marko.js +43 -0
  103. package/components/element/content/start-date.marko +17 -0
  104. package/components/element/content/start-date.marko.js +43 -0
  105. package/components/element/content/starts.marko +14 -0
  106. package/components/element/content/starts.marko.js +40 -0
  107. package/components/element/content/teaser.marko +15 -0
  108. package/components/element/content/teaser.marko.js +41 -0
  109. package/components/element/content/title.marko +14 -0
  110. package/components/element/content/title.marko.js +40 -0
  111. package/components/element/content/tollfree.marko +14 -0
  112. package/components/element/content/tollfree.marko.js +40 -0
  113. package/components/element/content/transcript.marko +22 -0
  114. package/components/element/content/transcript.marko.js +53 -0
  115. package/components/element/content/website.marko +22 -0
  116. package/components/element/content/website.marko.js +51 -0
  117. package/components/element/date.marko +2 -0
  118. package/components/element/date.marko.js +27 -0
  119. package/components/element/extract-render.js +1 -0
  120. package/components/element/image/caption.marko +15 -0
  121. package/components/element/image/caption.marko.js +41 -0
  122. package/components/element/image/credit.marko +15 -0
  123. package/components/element/image/credit.marko.js +41 -0
  124. package/components/element/image/display-name.marko +14 -0
  125. package/components/element/image/display-name.marko.js +40 -0
  126. package/components/element/image/marko.json +38 -0
  127. package/components/element/image/slider.marko +18 -0
  128. package/components/element/image/slider.marko.js +50 -0
  129. package/components/element/img.marko +12 -0
  130. package/components/element/img.marko.js +43 -0
  131. package/components/element/index.marko +12 -0
  132. package/components/element/index.marko.js +47 -0
  133. package/components/element/link.marko +25 -0
  134. package/components/element/link.marko.js +54 -0
  135. package/components/element/magazine-issue/description.marko +14 -0
  136. package/components/element/magazine-issue/description.marko.js +40 -0
  137. package/components/element/magazine-issue/digital-edition-url.marko +22 -0
  138. package/components/element/magazine-issue/digital-edition-url.marko.js +51 -0
  139. package/components/element/magazine-issue/marko.json +42 -0
  140. package/components/element/magazine-issue/name.marko +14 -0
  141. package/components/element/magazine-issue/name.marko.js +40 -0
  142. package/components/element/magazine-issue/pdf-url.marko +22 -0
  143. package/components/element/magazine-issue/pdf-url.marko.js +51 -0
  144. package/components/element/magazine-publication/cancel-url.marko +22 -0
  145. package/components/element/magazine-publication/cancel-url.marko.js +51 -0
  146. package/components/element/magazine-publication/change-address-url.marko +22 -0
  147. package/components/element/magazine-publication/change-address-url.marko.js +51 -0
  148. package/components/element/magazine-publication/description.marko +14 -0
  149. package/components/element/magazine-publication/description.marko.js +40 -0
  150. package/components/element/magazine-publication/einquiry-url.marko +22 -0
  151. package/components/element/magazine-publication/einquiry-url.marko.js +51 -0
  152. package/components/element/magazine-publication/marko.json +82 -0
  153. package/components/element/magazine-publication/name.marko +14 -0
  154. package/components/element/magazine-publication/name.marko.js +40 -0
  155. package/components/element/magazine-publication/renewal-url.marko +22 -0
  156. package/components/element/magazine-publication/renewal-url.marko.js +51 -0
  157. package/components/element/magazine-publication/reprints-url.marko +22 -0
  158. package/components/element/magazine-publication/reprints-url.marko.js +51 -0
  159. package/components/element/magazine-publication/subscribe-url.marko +22 -0
  160. package/components/element/magazine-publication/subscribe-url.marko.js +51 -0
  161. package/components/element/marko.json +211 -0
  162. package/components/element/obj-array.marko +17 -0
  163. package/components/element/obj-array.marko.js +56 -0
  164. package/components/element/obj-audio.marko +20 -0
  165. package/components/element/obj-audio.marko.js +50 -0
  166. package/components/element/obj-date.marko +25 -0
  167. package/components/element/obj-date.marko.js +55 -0
  168. package/components/element/obj-nodes.marko +20 -0
  169. package/components/element/obj-nodes.marko.js +62 -0
  170. package/components/element/obj-text.marko +23 -0
  171. package/components/element/obj-text.marko.js +53 -0
  172. package/components/element/obj.marko +23 -0
  173. package/components/element/obj.marko.js +61 -0
  174. package/components/element/object-link.js +49 -0
  175. package/components/element/object-type.js +9 -0
  176. package/components/element/picture.marko +38 -0
  177. package/components/element/picture.marko.js +85 -0
  178. package/components/element/show-element.js +8 -0
  179. package/components/element/text.marko +37 -0
  180. package/components/element/text.marko.js +76 -0
  181. package/components/element/website-section/description.marko +16 -0
  182. package/components/element/website-section/description.marko.js +44 -0
  183. package/components/element/website-section/hierarchy.marko +19 -0
  184. package/components/element/website-section/hierarchy.marko.js +60 -0
  185. package/components/element/website-section/marko.json +31 -0
  186. package/components/element/website-section/name.marko +18 -0
  187. package/components/element/website-section/name.marko.js +46 -0
  188. package/components/font/google.marko +6 -0
  189. package/components/font/google.marko.js +41 -0
  190. package/components/font/link.marko +14 -0
  191. package/components/font/link.marko.js +45 -0
  192. package/components/font/marko.json +21 -0
  193. package/components/font/typekit.marko +5 -0
  194. package/components/font/typekit.marko.js +38 -0
  195. package/components/load-more/index.marko +53 -0
  196. package/components/load-more/index.marko.js +119 -0
  197. package/components/load-more/marko.json +30 -0
  198. package/components/load-more/trigger.marko +11 -0
  199. package/components/load-more/trigger.marko.js +40 -0
  200. package/components/marko.json +37 -0
  201. package/components/node/body.marko +40 -0
  202. package/components/node/body.marko.js +112 -0
  203. package/components/node/element.marko +6 -0
  204. package/components/node/element.marko.js +39 -0
  205. package/components/node/footer.marko +19 -0
  206. package/components/node/footer.marko.js +63 -0
  207. package/components/node/header.marko +19 -0
  208. package/components/node/header.marko.js +63 -0
  209. package/components/node/image-inner-wrapper.marko +61 -0
  210. package/components/node/image-inner-wrapper.marko.js +116 -0
  211. package/components/node/image-wrapper.marko +34 -0
  212. package/components/node/image-wrapper.marko.js +98 -0
  213. package/components/node/image.marko +51 -0
  214. package/components/node/image.marko.js +93 -0
  215. package/components/node/index.marko +35 -0
  216. package/components/node/index.marko.js +100 -0
  217. package/components/node/marko.json +189 -0
  218. package/components/node/utils/image-height.js +3 -0
  219. package/components/node/utils/image-values.js +17 -0
  220. package/components/node-list/body.marko +19 -0
  221. package/components/node-list/body.marko.js +50 -0
  222. package/components/node-list/element.marko +3 -0
  223. package/components/node-list/element.marko.js +35 -0
  224. package/components/node-list/footer.marko +19 -0
  225. package/components/node-list/footer.marko.js +50 -0
  226. package/components/node-list/header.marko +19 -0
  227. package/components/node-list/header.marko.js +50 -0
  228. package/components/node-list/index.marko +36 -0
  229. package/components/node-list/index.marko.js +93 -0
  230. package/components/node-list/marko.json +121 -0
  231. package/components/node-list/node.marko +15 -0
  232. package/components/node-list/node.marko.js +49 -0
  233. package/components/node-list/nodes.marko +82 -0
  234. package/components/node-list/nodes.marko.js +126 -0
  235. package/components/page/container.marko +13 -0
  236. package/components/page/container.marko.js +46 -0
  237. package/components/page/description.marko +4 -0
  238. package/components/page/description.marko.js +26 -0
  239. package/components/page/image.marko +50 -0
  240. package/components/page/image.marko.js +122 -0
  241. package/components/page/layouts/content.marko +23 -0
  242. package/components/page/layouts/content.marko.js +63 -0
  243. package/components/page/layouts/default.marko +20 -0
  244. package/components/page/layouts/default.marko.js +61 -0
  245. package/components/page/layouts/dynamic-page.marko +19 -0
  246. package/components/page/layouts/dynamic-page.marko.js +59 -0
  247. package/components/page/layouts/magazine-issue.marko +19 -0
  248. package/components/page/layouts/magazine-issue.marko.js +59 -0
  249. package/components/page/layouts/magazine-publication.marko +19 -0
  250. package/components/page/layouts/magazine-publication.marko.js +59 -0
  251. package/components/page/layouts/marko.json +86 -0
  252. package/components/page/layouts/website-section.marko +25 -0
  253. package/components/page/layouts/website-section.marko.js +78 -0
  254. package/components/page/marko.json +93 -0
  255. package/components/page/metadata/components/common.marko +54 -0
  256. package/components/page/metadata/components/common.marko.js +117 -0
  257. package/components/page/metadata/content.marko +147 -0
  258. package/components/page/metadata/content.marko.js +216 -0
  259. package/components/page/metadata/default.marko +2 -0
  260. package/components/page/metadata/default.marko.js +29 -0
  261. package/components/page/metadata/dynamic-page.marko +40 -0
  262. package/components/page/metadata/dynamic-page.marko.js +84 -0
  263. package/components/page/metadata/google-structured-data/content.js +138 -0
  264. package/components/page/metadata/magazine-issue.marko +39 -0
  265. package/components/page/metadata/magazine-issue.marko.js +83 -0
  266. package/components/page/metadata/magazine-publication.marko +35 -0
  267. package/components/page/metadata/magazine-publication.marko.js +79 -0
  268. package/components/page/metadata/marko.json +32 -0
  269. package/components/page/metadata/website-section.marko +48 -0
  270. package/components/page/metadata/website-section.marko.js +92 -0
  271. package/components/page/rel-canonical.marko +13 -0
  272. package/components/page/rel-canonical.marko.js +42 -0
  273. package/components/page/title.marko +18 -0
  274. package/components/page/title.marko.js +46 -0
  275. package/components/page/wrapper.marko +16 -0
  276. package/components/page/wrapper.marko.js +63 -0
  277. package/components/resolve/marko.json +6 -0
  278. package/components/resolve/page.marko +10 -0
  279. package/components/resolve/page.marko.js +49 -0
  280. package/components/rss/marko.json +10 -0
  281. package/components/rss/website-section.marko +17 -0
  282. package/components/rss/website-section.marko.js +46 -0
  283. package/config/abstract-config.js +42 -0
  284. package/config/core.js +48 -0
  285. package/config/site.js +6 -0
  286. package/express/apollo.js +34 -0
  287. package/express/asset-loader.js +55 -0
  288. package/express/cdn.js +27 -0
  289. package/express/css-loader.js +117 -0
  290. package/express/embedded-media.js +31 -0
  291. package/express/error-handlers.js +113 -0
  292. package/express/find-content-alias.js +30 -0
  293. package/express/get-redirect.js +44 -0
  294. package/express/graphql-proxy.js +17 -0
  295. package/express/index.js +153 -0
  296. package/express/json-error-handler.js +30 -0
  297. package/express/load-document.js +3 -0
  298. package/express/load-more.js +6 -0
  299. package/express/load-object.js +10 -0
  300. package/express/oembed.js +18 -0
  301. package/express/rss.js +19 -0
  302. package/express/sitemaps.js +16 -0
  303. package/express/website-context.js +19 -0
  304. package/index.js +8 -0
  305. package/integration/test-website-boot.js +198 -0
  306. package/marko.json +6 -0
  307. package/middleware/disabled-features.js +15 -0
  308. package/middleware/index.js +15 -0
  309. package/middleware/page-node/index.js +45 -0
  310. package/middleware/page-node/resolved.js +26 -0
  311. package/middleware/with-content.js +54 -0
  312. package/middleware/with-dynamic-page.js +37 -0
  313. package/middleware/with-load-more.js +9 -0
  314. package/middleware/with-magazine-issue.js +24 -0
  315. package/middleware/with-magazine-publication.js +24 -0
  316. package/middleware/with-website-section.js +47 -0
  317. package/package.json +64 -0
  318. package/scss/carousel.scss +42 -0
  319. package/scss/document.scss +55 -0
  320. package/scss/fonts/alata-fallback.scss +7 -0
  321. package/scss/fonts/alegreya-fallback.scss +6 -0
  322. package/scss/fonts/arimo-fallback.scss +7 -0
  323. package/scss/fonts/fira-sans-fallback.scss +7 -0
  324. package/scss/fonts/ibm-plex-sans-fallback.scss +7 -0
  325. package/scss/fonts/informapro-fallback.scss +6 -0
  326. package/scss/fonts/lato-fallback.scss +7 -0
  327. package/scss/fonts/manrope-fallback.scss +6 -0
  328. package/scss/fonts/merriweather-fallback.scss +7 -0
  329. package/scss/fonts/montserrat-fallback.scss +7 -0
  330. package/scss/fonts/open-sans-condensed-fallback.scss +6 -0
  331. package/scss/fonts/open-sans-fallback.scss +7 -0
  332. package/scss/fonts/oswald-fallback.scss +7 -0
  333. package/scss/fonts/poppins-fallback.scss +6 -0
  334. package/scss/fonts/pt-sans-fallback.scss +6 -0
  335. package/scss/fonts/pt-serif-fallback.scss +6 -0
  336. package/scss/fonts/public-sans-fallback.scss +7 -0
  337. package/scss/fonts/raleway-fallback.scss +6 -0
  338. package/scss/fonts/roboto-condensed-fallback.scss +7 -0
  339. package/scss/fonts/roboto-fallback.scss +7 -0
  340. package/scss/fonts/roboto-slab-fallback.scss +7 -0
  341. package/scss/fonts/solitaire-mvb-pro-fallback.scss +6 -0
  342. package/scss/fonts/source-sans-4-fallback.scss +7 -0
  343. package/scss/fonts/source-serif-pro-fallback.scss +7 -0
  344. package/scss/fonts/synthese-fallback.scss +6 -0
  345. package/scss/fonts/zilla-slab-fallback.scss +6 -0
  346. package/scss/load-more.scss +3 -0
  347. package/scss/node-list.scss +144 -0
  348. package/scss/node.scss +371 -0
  349. package/scss/page-image.scss +15 -0
  350. package/start-server.js +169 -0
  351. package/utils/apply-query-params.js +16 -0
  352. package/utils/build-content-input.js +9 -0
  353. package/utils/build-marko-global.js +1 -0
  354. package/utils/default-value.js +1 -0
  355. package/utils/embedded-media/facebook-oembed.js +50 -0
  356. package/utils/embedded-media/image.js +71 -0
  357. package/utils/embedded-media/index.js +8 -0
  358. package/utils/embedded-media/instagram-oembed.js +57 -0
  359. package/utils/embedded-media/oembed.js +18 -0
  360. package/utils/hierarchy-aliases.js +3 -0
  361. package/utils/hierarchy-ids.js +3 -0
  362. package/utils/index.js +11 -0
  363. package/utils/is-dev.js +1 -0
  364. package/utils/published-content/description.js +1 -0
  365. package/utils/render-ssr-component-from-marko.js +18 -0
  366. package/utils/render-ssr-component.js +15 -0
  367. package/utils/should-collapse.js +1 -0
@@ -0,0 +1,198 @@
1
+ const { htmlEntities } = require('@mindful-web/html');
2
+ const { sleep: wait, cleanPath } = require('@mindful-web/utils');
3
+ const whilst = require('async/whilst');
4
+ const eachLimit = require('async/eachLimit');
5
+ const fetch = require('node-fetch');
6
+ const cheerio = require('cheerio');
7
+ const { inspect } = require('util');
8
+
9
+ const { log } = console;
10
+
11
+ const origin = process.env.MARKO_WEB_INTEGRATION_TEST_URL || 'http://localhost:80';
12
+
13
+ const fetchResponse = async ({
14
+ path = '/',
15
+ catchErrors = false,
16
+ followRedirects = false,
17
+ } = {}) => {
18
+ const url = `${cleanPath(origin)}/${cleanPath(path)}`;
19
+ log(`fetching ${url}`);
20
+ const opts = {
21
+ method: 'get',
22
+ redirect: followRedirects ? 'follow' : 'manual',
23
+ };
24
+ if (!catchErrors) return fetch(url, opts);
25
+ try {
26
+ const res = await fetch(url, opts);
27
+ return res;
28
+ } catch (e) {
29
+ return null;
30
+ }
31
+ };
32
+
33
+ const checkReadiness = async ({
34
+ path = '/_health',
35
+ startAfter = 5000,
36
+ checkInterval = 2000,
37
+ unhealthyAfter = 5,
38
+ } = {}) => {
39
+ log('checking readiness...');
40
+ log(`initially waiting for ${startAfter}ms before checking...`);
41
+ await wait(startAfter);
42
+
43
+ let timesChecked = 0;
44
+ let booted = false;
45
+ await whilst(async () => !booted, async () => {
46
+ timesChecked += 1;
47
+ if (timesChecked > unhealthyAfter) {
48
+ throw new Error('The readiness probe has reached its maximum check limit.');
49
+ }
50
+ log(`pinging health check, attempt number ${timesChecked}...`);
51
+ const res = await fetchResponse({ path, catchErrors: true });
52
+ // if response is null, a connection error occrred
53
+ if (res == null || (res && !res.ok)) {
54
+ if (res == null) log('did not receive a response - assuming not ready.');
55
+ if (res && !res.ok) log(`received a non-ok response from health check - ${res.status} ${res.statusText}`);
56
+ log(`waiting another ${checkInterval}ms before retrying.`);
57
+ await wait(checkInterval);
58
+ return;
59
+ }
60
+ booted = true;
61
+ });
62
+ log('container is ready.');
63
+ };
64
+
65
+ const testPage = async ({
66
+ path,
67
+ retryAttempts = 3,
68
+ serverErrorsOnly = true,
69
+ followRedirects = false,
70
+ checkInterval = 500,
71
+ } = {}) => {
72
+ let timesChecked = 0;
73
+ let finished = false;
74
+ let html;
75
+ const report = { path, error: null, checks: [] };
76
+
77
+ const waitThenRetry = async () => {
78
+ log(`waiting ${checkInterval}ms to retry ${path}`);
79
+ await wait(checkInterval);
80
+ };
81
+
82
+ await whilst(async () => !finished, async () => {
83
+ const check = {};
84
+ report.checks.push(check);
85
+ timesChecked += 1;
86
+ if (timesChecked > retryAttempts) {
87
+ // max times reach, append error to report.
88
+ report.error = new Error(`The test runner for page path ${path} has reached its maximum check limit.`);
89
+ finished = true;
90
+ return true;
91
+ }
92
+ const res = await fetchResponse({ path, followRedirects });
93
+ check.statusCode = res.status;
94
+ if (!res.ok) {
95
+ if (serverErrorsOnly && res.status < 500) {
96
+ check.message = `received a ${res.status} from path ${path} but treating as passing since it was not a server error (>= 500).`;
97
+ finished = true;
98
+ return true;
99
+ }
100
+ // received a 500, append the message and retry
101
+ check.message = `received an error response from path page ${path} with status ${res.status} ${res.statusText}`;
102
+ return waitThenRetry();
103
+ }
104
+ html = await res.text();
105
+
106
+ // first ensure the entire page rendered. if it didn't a fatal backened error occurred
107
+ // that prevented rendering, or some kind of timeout occurred. this can be retried.
108
+ const found = /.*<\/head>.*<\/body>.*<\/html>.*/is.test(html);
109
+ if (!found) {
110
+ // no render, append the message and retry.
111
+ check.message = `unable to detect a full page render for path ${path} retrying...`;
112
+ return waitThenRetry();
113
+ }
114
+
115
+ // then check for in-body errors. this means an async internal block failed
116
+ // but the page could fully render.
117
+ const matches = [...html.matchAll(/data-marko-error="(.*?)"/g)];
118
+ check.inPageErrors = [];
119
+ matches.forEach((values) => {
120
+ const value = values[1];
121
+ check.inPageErrors.push(htmlEntities.decode(value));
122
+ });
123
+ if (check.inPageErrors.length) {
124
+ // in-page errors occurred. exit and retry
125
+ return waitThenRetry();
126
+ }
127
+ // otherwise, mark as finished.
128
+ finished = true;
129
+ return true;
130
+ });
131
+ return { html, report };
132
+ };
133
+
134
+ const run = async () => {
135
+ await checkReadiness();
136
+
137
+ // test homepage first, and get html, allowing redirects (since this page _has_ to load).
138
+ const { html, report: initialReport } = await testPage({
139
+ path: '/',
140
+ serverErrorsOnly: false,
141
+ followRedirects: true,
142
+ });
143
+
144
+ const toTest = new Map([
145
+ ['/search', {}],
146
+ ['/site-map', {}], //
147
+ ]);
148
+ const contentToTest = new Map();
149
+
150
+ const $ = cheerio.load(html);
151
+ const $a = $('a[href^="/"]');
152
+
153
+ $a.each(function getHref() {
154
+ const href = $(this).attr('href');
155
+ if (toTest.has(href) || href === '/') return;
156
+ if (/\/\d{8}\//.test(href)) {
157
+ // content page
158
+ if (contentToTest.size === 5) return;
159
+ contentToTest.set(href, {});
160
+ return;
161
+ }
162
+ // non content pages
163
+ if (toTest.size === 10) return;
164
+ toTest.set(href, {});
165
+ });
166
+
167
+ // now test all extracted pages.
168
+ const errors = [];
169
+ const reports = [initialReport];
170
+ await eachLimit([...toTest, ...contentToTest], 2, async ([path, opts]) => {
171
+ // catch all errors so they can be reported on at once.
172
+ // these should be internal at this point.
173
+ try {
174
+ const { report } = await testPage({ ...opts, path });
175
+ reports.push(report);
176
+ } catch (e) {
177
+ errors.push(e);
178
+ }
179
+ });
180
+
181
+ if (errors.length) {
182
+ log('INTERNAL TESTING ERRORS ENCOUNTERED');
183
+ errors.forEach((error) => {
184
+ log(error);
185
+ });
186
+ throw new Error('Internal testing errors were encountered.');
187
+ }
188
+
189
+ const errorReports = reports.filter((report) => report.error);
190
+ if (errorReports.length) {
191
+ log('PAGE ERRORS ENCOUNTERED');
192
+ errorReports.forEach((report) => log(inspect(report, { colors: true, depth: null })));
193
+ throw new Error('Page errors were encountered.');
194
+ }
195
+ log('test results', inspect(reports, { colors: true, depth: null }));
196
+ };
197
+
198
+ run().catch((e) => setImmediate(() => { throw e; }));
package/marko.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "taglib-id": "@mindful-web/marko-web",
3
+ "taglib-imports": [
4
+ "./components/marko.json"
5
+ ]
6
+ }
@@ -0,0 +1,15 @@
1
+ module.exports = () => ((req, res, next) => {
2
+ const { disabledFeatures } = req.cookies;
3
+ res.locals.disabledFeatures = new Set();
4
+ if (disabledFeatures) {
5
+ try {
6
+ disabledFeatures.split(',').forEach((flag) => {
7
+ const trimmed = flag.trim();
8
+ if (trimmed) res.locals.disabledFeatures.add(trimmed);
9
+ });
10
+ } catch (e) {
11
+ next();
12
+ }
13
+ }
14
+ next();
15
+ });
@@ -0,0 +1,15 @@
1
+ const withContent = require('./with-content');
2
+ const withDynamicPage = require('./with-dynamic-page');
3
+ const withLoadMore = require('./with-load-more');
4
+ const withWebsiteSection = require('./with-website-section');
5
+ const withMagazineIssue = require('./with-magazine-issue');
6
+ const withMagazinePublication = require('./with-magazine-publication');
7
+
8
+ module.exports = {
9
+ withContent,
10
+ withDynamicPage,
11
+ withLoadMore,
12
+ withWebsiteSection,
13
+ withMagazineIssue,
14
+ withMagazinePublication,
15
+ };
@@ -0,0 +1,45 @@
1
+ const { isFunction: isFn } = require('@mindful-web/utils');
2
+ const { getAsObject } = require('@mindful-web/object-path');
3
+ const ResolvedNode = require('./resolved');
4
+
5
+ const createNode = (data) => new ResolvedNode(data);
6
+
7
+ class PageNode {
8
+ constructor(apolloClient, {
9
+ queryFactory,
10
+ queryFragment,
11
+ variables,
12
+ resultField,
13
+ sideloadDataFn,
14
+ }) {
15
+ this.apolloClient = apolloClient;
16
+ this.queryFactory = queryFactory;
17
+ this.queryFragment = queryFragment;
18
+ this.variables = variables;
19
+ this.resultField = resultField;
20
+ this.sideloadDataFn = sideloadDataFn;
21
+ }
22
+
23
+ async load() {
24
+ if (!this.promise) {
25
+ const { queryFragment, variables, resultField } = this;
26
+ const path = `data.${resultField}`;
27
+ const query = this.queryFactory({ queryFragment, queryName: 'PageNode' });
28
+ const [r, sideloadedData] = await Promise.all([
29
+ this.apolloClient.query({ query, variables }),
30
+ (async () => {
31
+ if (isFn(this.sideloadDataFn)) {
32
+ this.sideloadDataFn.bind(this);
33
+ return this.sideloadDataFn();
34
+ }
35
+ return undefined;
36
+ })(),
37
+ ]);
38
+ this.promise = createNode(getAsObject(r, path));
39
+ this.sideloadedData = sideloadedData;
40
+ }
41
+ return this.promise;
42
+ }
43
+ }
44
+
45
+ module.exports = PageNode;
@@ -0,0 +1,26 @@
1
+ const { get, getAsObject, getAsArray } = require('@mindful-web/object-path');
2
+ const { asObject } = require('@mindful-web/utils');
3
+
4
+ class ResolvedNode {
5
+ constructor(data) {
6
+ this.data = data;
7
+ }
8
+
9
+ get(path, def) {
10
+ return get(this.data, path, def);
11
+ }
12
+
13
+ getAsObject(path) {
14
+ return getAsObject(this.data, path);
15
+ }
16
+
17
+ getAsArray(path) {
18
+ return getAsArray(this.data, path);
19
+ }
20
+
21
+ getEdgeNodesFor(path) {
22
+ return this.getAsArray(`${path}.edges`).map((edge) => asObject(edge).node);
23
+ }
24
+ }
25
+
26
+ module.exports = ResolvedNode;
@@ -0,0 +1,54 @@
1
+ const createError = require('http-errors');
2
+ const { get } = require('@mindful-web/object-path');
3
+ const { asyncRoute, isFunction: isFn } = require('@mindful-web/utils');
4
+ const { content: loader } = require('@mindful-web/web-common/page-loaders');
5
+ const { blockContent: queryFactory } = require('@mindful-web/web-common/query-factories');
6
+ const setRouteKind = require('@mindful-web/marko-express/utils/set-route-kind');
7
+ const PageNode = require('./page-node');
8
+ const buildContentInput = require('../utils/build-content-input');
9
+ const applyQueryParams = require('../utils/apply-query-params');
10
+
11
+ module.exports = ({
12
+ template,
13
+ queryFragment,
14
+ idResolver,
15
+ redirectOnPathMismatch = true,
16
+ loaderQueryFragment,
17
+ redirectToFn,
18
+ pathFn,
19
+ formatResponse,
20
+ sideloadDataFn,
21
+ } = {}) => asyncRoute(async (req, res) => {
22
+ const id = isFn(idResolver) ? await idResolver(req, res) : req.params.id;
23
+ const { apollo, query } = req;
24
+
25
+ const additionalInput = buildContentInput({ req });
26
+ const content = await loader(apollo, { id, additionalInput, queryFragment: loaderQueryFragment });
27
+ const requestingSiteId = req.app.locals.config.website('id');
28
+
29
+ // set the route kind
30
+ setRouteKind(res, { kind: 'content', type: content.type });
31
+ const redirectTo = isFn(redirectToFn) ? redirectToFn({
32
+ content,
33
+ requestingSiteId,
34
+ }) : content.redirectTo;
35
+ const path = isFn(pathFn) ? pathFn({ content }) : get(content, 'siteContext.path');
36
+
37
+ if (redirectTo) {
38
+ return res.redirect(301, applyQueryParams({ path: redirectTo, query }));
39
+ }
40
+ if (redirectOnPathMismatch && path !== req.path) {
41
+ const pathTo = req.query['preview-mode'] ? `${path}?preview-mode=true` : path;
42
+ return res.redirect(301, applyQueryParams({ path: pathTo, query }));
43
+ }
44
+ if (content.deletedContent) throw createError(404, `No published content was found for id '${id}'`);
45
+ const pageNode = new PageNode(apollo, {
46
+ queryFactory,
47
+ queryFragment,
48
+ variables: { input: { id: Number(id), ...additionalInput } },
49
+ resultField: 'content',
50
+ sideloadDataFn,
51
+ });
52
+ if (isFn(formatResponse)) await formatResponse({ res, content, pageNode });
53
+ return res.marko(template, { ...content, pageNode });
54
+ });
@@ -0,0 +1,37 @@
1
+ const { get } = require('@mindful-web/object-path');
2
+ const { asyncRoute, isFunction: isFn } = require('@mindful-web/utils');
3
+ const { dynamicPage: loader } = require('@mindful-web/web-common/page-loaders');
4
+ const { blockDynamicPage: queryFactory } = require('@mindful-web/web-common/query-factories');
5
+ const setRouteKind = require('@mindful-web/marko-express/utils/set-route-kind');
6
+ const PageNode = require('./page-node');
7
+ const applyQueryParams = require('../utils/apply-query-params');
8
+
9
+ module.exports = ({
10
+ template,
11
+ queryFragment,
12
+ aliasResolver,
13
+ redirectOnPathMismatch = true,
14
+ } = {}) => asyncRoute(async (req, res) => {
15
+ const alias = isFn(aliasResolver) ? await aliasResolver(req, res) : req.params.alias;
16
+ const { apollo, query } = req;
17
+
18
+ const page = await loader(apollo, { alias });
19
+
20
+ setRouteKind(res, { kind: 'dynamic-page', type: alias });
21
+
22
+ const { redirectTo } = page;
23
+ const path = get(page, 'siteContext.path');
24
+ if (redirectTo) {
25
+ return res.redirect(301, applyQueryParams({ path: redirectTo, query }));
26
+ }
27
+ if (redirectOnPathMismatch && path !== req.path) {
28
+ return res.redirect(301, applyQueryParams({ path, query }));
29
+ }
30
+ const pageNode = new PageNode(apollo, {
31
+ queryFactory,
32
+ queryFragment,
33
+ variables: { input: { alias } },
34
+ resultField: 'contentPage',
35
+ });
36
+ return res.marko(template, { ...page, pageNode });
37
+ });
@@ -0,0 +1,9 @@
1
+ const { asyncRoute } = require('@mindful-web/utils');
2
+ const LoadMore = require('../components/load-more');
3
+
4
+ module.exports = () => asyncRoute(async (req, res) => {
5
+ const { method, query, body } = req;
6
+ const data = method === 'POST' ? JSON.parse(body) : query;
7
+ const input = JSON.parse(data.input || null) || {};
8
+ res.marko(LoadMore, input);
9
+ });
@@ -0,0 +1,24 @@
1
+ const { asyncRoute } = require('@mindful-web/utils');
2
+ const { magazineIssue: loader } = require('@mindful-web/web-common/page-loaders');
3
+ const { blockMagazineIssue: queryFactory } = require('@mindful-web/web-common/query-factories');
4
+ const setRouteKind = require('@mindful-web/marko-express/utils/set-route-kind');
5
+ const PageNode = require('./page-node');
6
+
7
+ module.exports = ({
8
+ template,
9
+ queryFragment,
10
+ } = {}) => asyncRoute(async (req, res) => {
11
+ const { apollo } = req;
12
+ const id = Number(req.params.id);
13
+ const issue = await loader(apollo, { id });
14
+
15
+ // set the route kind
16
+ setRouteKind(res, { kind: 'magazine-issue', type: '' });
17
+ const pageNode = new PageNode(apollo, {
18
+ queryFactory,
19
+ queryFragment,
20
+ variables: { input: { id } },
21
+ resultField: 'magazineIssue',
22
+ });
23
+ return res.marko(template, { ...issue, pageNode });
24
+ });
@@ -0,0 +1,24 @@
1
+ const { asyncRoute } = require('@mindful-web/utils');
2
+ const { magazinePublication: loader } = require('@mindful-web/web-common/page-loaders');
3
+ const { blockMagazinePublication: queryFactory } = require('@mindful-web/web-common/query-factories');
4
+ const setRouteKind = require('@mindful-web/marko-express/utils/set-route-kind');
5
+ const PageNode = require('./page-node');
6
+
7
+ module.exports = ({
8
+ template,
9
+ queryFragment,
10
+ } = {}) => asyncRoute(async (req, res) => {
11
+ const { apollo } = req;
12
+ const { id } = req.params;
13
+ const publication = await loader(apollo, { id });
14
+
15
+ // set the route kind
16
+ setRouteKind(res, { kind: 'magazine-publication', type: '' });
17
+ const pageNode = new PageNode(apollo, {
18
+ queryFactory,
19
+ queryFragment,
20
+ variables: { input: { id } },
21
+ resultField: 'magazinePublication',
22
+ });
23
+ return res.marko(template, { ...publication, pageNode });
24
+ });
@@ -0,0 +1,47 @@
1
+ const { asyncRoute, isFunction: isFn } = require('@mindful-web/utils');
2
+ const { websiteSection: loader } = require('@mindful-web/web-common/page-loaders');
3
+ const { blockWebsiteSection: queryFactory } = require('@mindful-web/web-common/query-factories');
4
+ const setRouteKind = require('@mindful-web/marko-express/utils/set-route-kind');
5
+ const PageNode = require('./page-node');
6
+ const applyQueryParams = require('../utils/apply-query-params');
7
+
8
+ module.exports = ({
9
+ template,
10
+ queryFragment,
11
+ aliasResolver,
12
+ redirectOnPathMismatch = true,
13
+ context: contextFn,
14
+ } = {}) => asyncRoute(async (req, res) => {
15
+ const alias = isFn(aliasResolver) ? await aliasResolver(req, res) : req.params.alias;
16
+ const { apollo, query } = req;
17
+ const cleanedAlias = alias.replace(/\/+$/, '').replace(/^\/+/, '');
18
+
19
+ const section = await loader(apollo, { alias: cleanedAlias });
20
+
21
+ // set the route kind
22
+ setRouteKind(res, { kind: 'website-section', type: section.alias });
23
+ const { redirectTo, canonicalPath } = section;
24
+ if (redirectTo) {
25
+ return res.redirect(301, applyQueryParams({ path: redirectTo, query }));
26
+ }
27
+ if (redirectOnPathMismatch && canonicalPath !== req.path) {
28
+ return res.redirect(301, applyQueryParams({ path: canonicalPath, query }));
29
+ }
30
+ const pageNode = new PageNode(apollo, {
31
+ queryFactory,
32
+ queryFragment,
33
+ variables: { input: { alias: cleanedAlias } },
34
+ resultField: 'websiteSectionAlias',
35
+ });
36
+
37
+ let context = {};
38
+ if (typeof contextFn === 'function') {
39
+ context = await contextFn({
40
+ req,
41
+ res,
42
+ section,
43
+ pageNode,
44
+ });
45
+ }
46
+ return res.marko(template, { ...section, pageNode, context });
47
+ });
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@mindful-web/marko-web",
3
+ "version": "1.0.0",
4
+ "description": "Core Marko+Express components for Mindful Web websites",
5
+ "author": "Jacob Bare <jacob@parameter1.com>",
6
+ "main": "index.js",
7
+ "license": "MIT",
8
+ "repository": "https://github.com/parameter1/mindful-web/tree/main/packages/marko-web",
9
+ "engines": {
10
+ "node": ">=14.15"
11
+ },
12
+ "os": [
13
+ "darwin",
14
+ "linux",
15
+ "win32"
16
+ ],
17
+ "scripts": {
18
+ "lint:fix": "yarn lint --fix",
19
+ "lint": "eslint --ext .js --ext .vue --max-warnings 5 ./",
20
+ "compile": "mindful-web-marko-compile compile",
21
+ "prepublish": "yarn compile --silent",
22
+ "test": "yarn compile --no-clean && yarn lint"
23
+ },
24
+ "dependencies": {
25
+ "@godaddy/terminus": "^4.11.2",
26
+ "@mindful-web/apollo-ssr": "^1.0.0",
27
+ "@mindful-web/dayjs": "^1.0.0",
28
+ "@mindful-web/embedded-media": "^1.0.0",
29
+ "@mindful-web/express-apollo": "^1.0.0",
30
+ "@mindful-web/graphql-fragment-types": "^1.0.0",
31
+ "@mindful-web/html": "^1.0.0",
32
+ "@mindful-web/image": "^1.0.0",
33
+ "@mindful-web/inflector": "^1.0.0",
34
+ "@mindful-web/marko-express": "^1.0.0",
35
+ "@mindful-web/marko-node-require": "^1.0.0",
36
+ "@mindful-web/marko-web-deferred-script-loader": "^1.0.0",
37
+ "@mindful-web/object-path": "^1.0.0",
38
+ "@mindful-web/tenant-context": "^1.0.0",
39
+ "@mindful-web/utils": "^1.0.0",
40
+ "@mindful-web/web-common": "^1.0.0",
41
+ "async": "^3.2.4",
42
+ "cheerio": "^1.0.0-rc.12",
43
+ "cookie-parser": "^1.4.6",
44
+ "express": "^4.18.2",
45
+ "express-http-proxy": "^1.6.3",
46
+ "graphql": "^14.7.0",
47
+ "graphql-tag": "^2.12.6",
48
+ "helmet": "^3.23.3",
49
+ "http-errors": "^1.8.1",
50
+ "jquery": "^3.6.3",
51
+ "marko": "~4.20.0",
52
+ "nanoid": "^3.3.4",
53
+ "node-fetch": "^2.6.9",
54
+ "vue": "^2.7.14",
55
+ "vue-server-renderer": "^2.7.14"
56
+ },
57
+ "peerDependencies": {
58
+ "@mindful-web/marko-core": "^0.0.0"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ },
63
+ "gitHead": "0b77cab713eb5841202bb86c7119949866bc68b5"
64
+ }
@@ -0,0 +1,42 @@
1
+ .carousel {
2
+ margin-bottom: .75rem;
3
+
4
+ .carousel-item {
5
+ img {
6
+ display: block;
7
+ width: 100%;
8
+ }
9
+ }
10
+
11
+ .carousel-caption {
12
+ right: 0;
13
+ bottom: 0;
14
+ left: 0;
15
+ z-index: initial;
16
+ padding-bottom: 1rem;
17
+ text-shadow: rgba(0, 0, 0, .6) 0 1px 2px;
18
+ background: rgba(0, 0, 0, .75);
19
+ p {
20
+ display: none;
21
+ @include media-breakpoint-up(md) {
22
+ display: block;
23
+ }
24
+ }
25
+ }
26
+
27
+ .carousel-indicators {
28
+ z-index: 2;
29
+ margin-bottom: 0;
30
+ }
31
+ }
32
+
33
+ // Carousel Overrides
34
+
35
+ .carousel-control-next,
36
+ .carousel-control-prev {
37
+ top: initial;
38
+ bottom: 8px;
39
+ z-index: 2;
40
+ justify-content: space-around;
41
+ opacity: initial;
42
+ }
@@ -0,0 +1,55 @@
1
+ $marko-web-document-container-gutter: .75rem !default;
2
+ $marko-web-document-container-max-width: 1200px !default;
3
+
4
+ $marko-web-page-padding: $marko-web-document-container-gutter !default;
5
+
6
+ $marko-web-page-wrapper-bg-color: #fff !default;
7
+ $marko-web-page-wrapper-padding: $marko-web-page-padding !default;
8
+ $marko-web-page-wrapper-section-margin: $marko-web-document-container-gutter !default;
9
+
10
+ html {
11
+ height: 100%;
12
+ }
13
+
14
+ body {
15
+ display: flex;
16
+ flex-direction: column;
17
+ height: 100%;
18
+ }
19
+
20
+ .document-container {
21
+ position: relative;
22
+ flex-grow: 1;
23
+ width: 100%;
24
+ padding-right: $marko-web-document-container-gutter;
25
+ padding-left: $marko-web-document-container-gutter;
26
+ margin-right: auto;
27
+ margin-left: auto;
28
+
29
+ @if $marko-web-document-container-max-width {
30
+ @media (min-width: $marko-web-document-container-max-width) {
31
+ max-width: $marko-web-document-container-max-width;
32
+ }
33
+ }
34
+ }
35
+
36
+ .page {
37
+ padding: $marko-web-page-padding;
38
+ &:first-child {
39
+ padding-top: $marko-web-page-padding * 2;
40
+ }
41
+ &:last-child {
42
+ padding-bottom: $marko-web-page-padding * 2;
43
+ }
44
+ }
45
+
46
+ .page-wrapper {
47
+ padding: $marko-web-page-wrapper-padding;
48
+ background-color: $marko-web-page-wrapper-bg-color;
49
+ &__section {
50
+ margin-bottom: $marko-web-page-wrapper-section-margin;
51
+ &:last-child {
52
+ margin-bottom: 0;
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,7 @@
1
+ @font-face {
2
+ /*! critical */
3
+ font-family: alata-fallback;
4
+ size-adjust: 102.30000000000004%;
5
+ ascent-override: 110%;
6
+ src: local("Arial");
7
+ }