@dwp/govuk-casa 8.7.12 → 8.9.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 (173) hide show
  1. package/dist/casa.d.ts +12 -1
  2. package/dist/casa.js +10 -2
  3. package/dist/casa.js.map +1 -0
  4. package/dist/lib/CasaTemplateLoader.js +1 -0
  5. package/dist/lib/CasaTemplateLoader.js.map +1 -0
  6. package/dist/lib/JourneyContext.d.ts +12 -3
  7. package/dist/lib/JourneyContext.js +20 -5
  8. package/dist/lib/JourneyContext.js.map +1 -0
  9. package/dist/lib/MutableRouter.js +1 -0
  10. package/dist/lib/MutableRouter.js.map +1 -0
  11. package/dist/lib/Plan.d.ts +1 -1
  12. package/dist/lib/Plan.js +2 -5
  13. package/dist/lib/Plan.js.map +1 -0
  14. package/dist/lib/ValidationError.js +1 -0
  15. package/dist/lib/ValidationError.js.map +1 -0
  16. package/dist/lib/ValidatorFactory.d.ts +2 -2
  17. package/dist/lib/ValidatorFactory.js +3 -2
  18. package/dist/lib/ValidatorFactory.js.map +1 -0
  19. package/dist/lib/configuration-ingestor.js +1 -0
  20. package/dist/lib/configuration-ingestor.js.map +1 -0
  21. package/dist/lib/configure.js +2 -1
  22. package/dist/lib/configure.js.map +1 -0
  23. package/dist/lib/constants.d.ts +9 -0
  24. package/dist/lib/constants.js +13 -0
  25. package/dist/lib/constants.js.map +1 -0
  26. package/dist/lib/end-session.js +1 -0
  27. package/dist/lib/end-session.js.map +1 -0
  28. package/dist/lib/field.js +1 -0
  29. package/dist/lib/field.js.map +1 -0
  30. package/dist/lib/index.js +1 -0
  31. package/dist/lib/index.js.map +1 -0
  32. package/dist/lib/logger.js +1 -0
  33. package/dist/lib/logger.js.map +1 -0
  34. package/dist/lib/mount.js +3 -2
  35. package/dist/lib/mount.js.map +1 -0
  36. package/dist/lib/nunjucks-filters.js +1 -0
  37. package/dist/lib/nunjucks-filters.js.map +1 -0
  38. package/dist/lib/nunjucks.js +1 -0
  39. package/dist/lib/nunjucks.js.map +1 -0
  40. package/dist/lib/utils.d.ts +45 -27
  41. package/dist/lib/utils.js +105 -67
  42. package/dist/lib/utils.js.map +1 -0
  43. package/dist/lib/validators/dateObject.js +4 -3
  44. package/dist/lib/validators/dateObject.js.map +1 -0
  45. package/dist/lib/validators/email.js +1 -0
  46. package/dist/lib/validators/email.js.map +1 -0
  47. package/dist/lib/validators/inArray.js +1 -0
  48. package/dist/lib/validators/inArray.js.map +1 -0
  49. package/dist/lib/validators/index.js +1 -0
  50. package/dist/lib/validators/index.js.map +1 -0
  51. package/dist/lib/validators/nino.js +1 -0
  52. package/dist/lib/validators/nino.js.map +1 -0
  53. package/dist/lib/validators/postalAddressObject.d.ts +2 -2
  54. package/dist/lib/validators/postalAddressObject.js +2 -1
  55. package/dist/lib/validators/postalAddressObject.js.map +1 -0
  56. package/dist/lib/validators/regex.js +1 -0
  57. package/dist/lib/validators/regex.js.map +1 -0
  58. package/dist/lib/validators/required.js +1 -0
  59. package/dist/lib/validators/required.js.map +1 -0
  60. package/dist/lib/validators/strlen.js +1 -0
  61. package/dist/lib/validators/strlen.js.map +1 -0
  62. package/dist/lib/validators/wordCount.js +1 -0
  63. package/dist/lib/validators/wordCount.js.map +1 -0
  64. package/dist/lib/waypoint-url.js +40 -9
  65. package/dist/lib/waypoint-url.js.map +1 -0
  66. package/dist/middleware/body-parser.js +1 -0
  67. package/dist/middleware/body-parser.js.map +1 -0
  68. package/dist/middleware/csrf.js +1 -0
  69. package/dist/middleware/csrf.js.map +1 -0
  70. package/dist/middleware/data.js +1 -0
  71. package/dist/middleware/data.js.map +1 -0
  72. package/dist/middleware/gather-fields.js +10 -2
  73. package/dist/middleware/gather-fields.js.map +1 -0
  74. package/dist/middleware/i18n.js +1 -0
  75. package/dist/middleware/i18n.js.map +1 -0
  76. package/dist/middleware/post.js +1 -0
  77. package/dist/middleware/post.js.map +1 -0
  78. package/dist/middleware/pre.js +1 -0
  79. package/dist/middleware/pre.js.map +1 -0
  80. package/dist/middleware/progress-journey.js +7 -2
  81. package/dist/middleware/progress-journey.js.map +1 -0
  82. package/dist/middleware/sanitise-fields.js +1 -0
  83. package/dist/middleware/sanitise-fields.js.map +1 -0
  84. package/dist/middleware/serve-first-waypoint.js +1 -0
  85. package/dist/middleware/serve-first-waypoint.js.map +1 -0
  86. package/dist/middleware/session.js +1 -0
  87. package/dist/middleware/session.js.map +1 -0
  88. package/dist/middleware/skip-waypoint.js +1 -0
  89. package/dist/middleware/skip-waypoint.js.map +1 -0
  90. package/dist/middleware/steer-journey.js +2 -0
  91. package/dist/middleware/steer-journey.js.map +1 -0
  92. package/dist/middleware/strip-proxy-path.js +1 -0
  93. package/dist/middleware/strip-proxy-path.js.map +1 -0
  94. package/dist/middleware/validate-fields.js +7 -1
  95. package/dist/middleware/validate-fields.js.map +1 -0
  96. package/dist/mjs/esm-wrapper.js +11 -15
  97. package/dist/routes/ancillary.js +1 -0
  98. package/dist/routes/ancillary.js.map +1 -0
  99. package/dist/routes/journey.js +1 -0
  100. package/dist/routes/journey.js.map +1 -0
  101. package/dist/routes/static.js +1 -0
  102. package/dist/routes/static.js.map +1 -0
  103. package/locales/cy/error.json +1 -1
  104. package/locales/en/error.json +1 -1
  105. package/package.json +17 -16
  106. package/src/casa.js +330 -0
  107. package/src/lib/CasaTemplateLoader.js +104 -0
  108. package/src/lib/JourneyContext.js +797 -0
  109. package/src/lib/MutableRouter.js +310 -0
  110. package/src/lib/Plan.js +619 -0
  111. package/src/lib/ValidationError.js +163 -0
  112. package/src/lib/ValidatorFactory.js +105 -0
  113. package/src/lib/configuration-ingestor.js +457 -0
  114. package/src/lib/configure.js +202 -0
  115. package/src/lib/constants.js +9 -0
  116. package/src/lib/dirname.cjs +1 -0
  117. package/src/lib/end-session.js +45 -0
  118. package/src/lib/field.js +456 -0
  119. package/src/lib/index.js +33 -0
  120. package/src/lib/logger.js +16 -0
  121. package/src/lib/mount.js +127 -0
  122. package/src/lib/nunjucks-filters.js +150 -0
  123. package/src/lib/nunjucks.js +53 -0
  124. package/src/lib/utils.js +232 -0
  125. package/src/lib/validators/dateObject.js +169 -0
  126. package/src/lib/validators/email.js +55 -0
  127. package/src/lib/validators/inArray.js +81 -0
  128. package/src/lib/validators/index.js +24 -0
  129. package/src/lib/validators/nino.js +57 -0
  130. package/src/lib/validators/postalAddressObject.js +162 -0
  131. package/src/lib/validators/regex.js +48 -0
  132. package/src/lib/validators/required.js +74 -0
  133. package/src/lib/validators/strlen.js +66 -0
  134. package/src/lib/validators/wordCount.js +70 -0
  135. package/src/lib/waypoint-url.js +126 -0
  136. package/src/middleware/body-parser.js +31 -0
  137. package/src/middleware/csrf.js +29 -0
  138. package/src/middleware/data.js +105 -0
  139. package/src/middleware/dirname.cjs +1 -0
  140. package/src/middleware/gather-fields.js +58 -0
  141. package/src/middleware/i18n.js +106 -0
  142. package/src/middleware/post.js +61 -0
  143. package/src/middleware/pre.js +91 -0
  144. package/src/middleware/progress-journey.js +96 -0
  145. package/src/middleware/sanitise-fields.js +58 -0
  146. package/src/middleware/serve-first-waypoint.js +28 -0
  147. package/src/middleware/session.js +129 -0
  148. package/src/middleware/skip-waypoint.js +46 -0
  149. package/src/middleware/steer-journey.js +79 -0
  150. package/src/middleware/strip-proxy-path.js +56 -0
  151. package/src/middleware/validate-fields.js +89 -0
  152. package/src/routes/ancillary.js +29 -0
  153. package/src/routes/dirname.cjs +1 -0
  154. package/src/routes/journey.js +212 -0
  155. package/src/routes/static.js +77 -0
  156. package/views/casa/components/character-count/README.md +10 -0
  157. package/views/casa/components/character-count/template.njk +6 -2
  158. package/views/casa/components/checkboxes/README.md +43 -34
  159. package/views/casa/components/checkboxes/template.njk +8 -7
  160. package/views/casa/components/date-input/README.md +11 -1
  161. package/views/casa/components/date-input/template.njk +6 -4
  162. package/views/casa/components/input/README.md +9 -0
  163. package/views/casa/components/input/template.njk +6 -2
  164. package/views/casa/components/postal-address-object/README.md +10 -0
  165. package/views/casa/components/postal-address-object/template.njk +20 -5
  166. package/views/casa/components/radios/README.md +49 -24
  167. package/views/casa/components/radios/template.njk +6 -3
  168. package/views/casa/components/select/README.md +65 -0
  169. package/views/casa/components/select/macro.njk +3 -0
  170. package/views/casa/components/select/template.njk +49 -0
  171. package/views/casa/components/textarea/README.md +9 -0
  172. package/views/casa/components/textarea/template.njk +6 -2
  173. package/views/casa/layouts/journey.njk +1 -1
package/src/casa.js ADDED
@@ -0,0 +1,330 @@
1
+ // NOTE: Any changes made here must be reflected in `scripts/esm-wrapper.js`
2
+ import configure from './lib/configure.js';
3
+ import validators from './lib/validators/index.js';
4
+ import field from './lib/field.js';
5
+ import Plan from './lib/Plan.js';
6
+ import JourneyContext from './lib/JourneyContext.js';
7
+ import ValidatorFactory from './lib/ValidatorFactory.js';
8
+ import ValidationError from './lib/ValidationError.js';
9
+ import MutableRouter from './lib/MutableRouter.js';
10
+ import waypointUrl from './lib/waypoint-url.js';
11
+ import endSession from './lib/end-session.js';
12
+ import * as nunjucksFilters from './lib/nunjucks-filters.js';
13
+ import * as constants from './lib/constants.js';
14
+
15
+ /** @module @dwp/govuk-casa */
16
+ export {
17
+ configure,
18
+ validators,
19
+ field,
20
+ Plan,
21
+ JourneyContext,
22
+ ValidatorFactory,
23
+ ValidationError,
24
+ MutableRouter,
25
+
26
+ // Utilities
27
+ waypointUrl,
28
+ endSession,
29
+
30
+ // Nunjucks filters
31
+ nunjucksFilters,
32
+
33
+ // Constants
34
+ constants,
35
+ };
36
+
37
+ /* ----------------------------------------------------------------- Typedefs */
38
+ // These exist here so that consumer can import CASA's internal types
39
+
40
+ /**
41
+ * @typedef {import('./lib/field').PageField} PageField
42
+ */
43
+
44
+ /**
45
+ * @typedef {object} ContextEventHandlerOptions
46
+ * @property {JourneyContext} journeyContext Context including changes
47
+ * @property {JourneyContext} previousContext Context prior to changes
48
+ * @property {object} session Request session object
49
+ * @property {ContextEventUserInfo} userInfo User-space information pass-through
50
+ */
51
+
52
+ /**
53
+ * @callback ContextEventHandler
54
+ * @param {ContextEventHandlerOptions} opts Options
55
+ * @returns {void}
56
+ */
57
+
58
+ /**
59
+ * @typedef {object} ContextEventUserInfo
60
+ * @property {symbol} [casaRequestPhase] Request phase at which event is triggered
61
+ */
62
+
63
+ /**
64
+ * @typedef {object} ContextEvent
65
+ * @property {string} waypoint Waypoint to watch for changes
66
+ * @property {string} [field] Field to watch for changes
67
+ * @property {ContextEventHandler} handler Handler to invoke when change happens
68
+ */
69
+
70
+ /**
71
+ * @typedef {object} Page Page configuration. A Page is the interactive representation of a waypoint
72
+ * @property {string} waypoint The waypoint with which this page is associated
73
+ * @property {string} view Template path
74
+ * @property {PageHook[]} [hooks=[]] Page-specific hooks (optional, default [])
75
+ * @property {PageField[]} [fields=[]] Fields to be managed on this page (optional, default [])
76
+ */
77
+
78
+ /**
79
+ * @typedef {object} I18nOptions
80
+ * @property {string[]} dirs Directories to search for locale dictionaries
81
+ * @property {string[]} [locales=['en', 'cy']] Supported locales
82
+ */
83
+
84
+ /**
85
+ * @typedef {object} GlobalHook Hook configuration
86
+ * @property {string} hook Hook name in format `<router>.<hook>`
87
+ * @property {Function} middleware Middleware function to insert at the hook point
88
+ * @property {string|RegExp} [path=undefined] Only run if route path matches this string/regexp
89
+ */
90
+
91
+ /**
92
+ * @typedef {object} PageHook (extends GlobalHook)
93
+ * @property {string} hook Hook name (without a scope prefix)
94
+ * @property {Function} middleware Middleware function to insert at the hook point
95
+ */
96
+
97
+ /**
98
+ * @typedef {object} SessionOptions
99
+ * @property {string} [name=casasession] Session name
100
+ * @property {string} [secret=secret] Encryption secret
101
+ * @property {number} [ttl=3600] Session ttl (seconds)
102
+ * @property {boolean} [secure=false] Whether to use secure session cookies
103
+ * @property {boolean|string} [cookieSameSite=true] SameSite (true = Strict)
104
+ * @property {object} [store] Session store (default MemoryStore)
105
+ * @property {string} [cookiePath] the URL path on which the session cookie
106
+ * is valid (defaults to '/')
107
+ */
108
+
109
+ /**
110
+ * @typedef {object} IPlugin Plugin interface
111
+ * @property {PluginConfigureFunction} [configure] Modify the app config
112
+ * @property {PluginBootstrapFunction} [bootstrap] Modify post-configuration artifacts
113
+ */
114
+
115
+ /**
116
+ * @callback PluginConfigureFunction
117
+ * @param {ConfigurationOptions} config Options
118
+ */
119
+
120
+ /**
121
+ * @callback PluginBootstrapFunction
122
+ * @param {ConfigureResult} config Options
123
+ */
124
+
125
+ /**
126
+ * @callback HelmetConfigurator
127
+ * @param {object} config A default Helmet configuration provided by CASA
128
+ * @returns {object} The modified configuration object
129
+ */
130
+
131
+ /**
132
+ * Mounting function.
133
+ *
134
+ * This will mount all of the routes and middleware in the correct order on
135
+ * the given ExpressJS app.
136
+ *
137
+ * Once this is called, you will not be able to modify any of the routers as
138
+ * they will be "sealed".
139
+ *
140
+ * @callback Mounter
141
+ * @param {import('express').Express} app Express application
142
+ * @param {object} opts Mounting options
143
+ * @param {string} [opts.route='/'] Optional route to attach all middleware/routers too
144
+ * @returns {import('express').Express} The prepared ExpressJS app instance
145
+ */
146
+
147
+ /**
148
+ * Configure some middleware for use in creating a new CASA app.
149
+ *
150
+ * @typedef {object} ConfigurationOptions Configuration options
151
+ * @property {string} [mountUrl] Prefix for all URLS in browser address bar
152
+ * @property {string[]} [views=[]] Template directories
153
+ * @property {SessionOptions} [session] Session configuration
154
+ * @property {Page[]} [pages=[]] Pages the represent waypoints
155
+ * @property {GlobalHook[]} [hooks=[]] Hooks to apply
156
+ * @property {IPlugin[]} [plugins=[]] Plugins
157
+ * @property {I18nOptions} [i18n] I18n configuration
158
+ * @property {Plan} [plan] CASA Plan
159
+ * @property {ContextEvent[]} [events=[]] Handlers for JourneyContext events
160
+ * @property {HelmetConfigurator} [helmetConfigurator] Helmet configuration manipulator function
161
+ */
162
+
163
+ /**
164
+ * @typedef {object} ConfigureResult Result of a call to configure() function
165
+ * @property {import('nunjucks').Environment} nunjucksEnv Nunjucks environment
166
+ * @property {MutableRouter} staticRouter Router handling all static assets
167
+ * @property {MutableRouter} ancillaryRouter Router handling ancillary routes
168
+ * @property {MutableRouter} journeyRouter Router handling all waypoint requests
169
+ * @property {import('express').RequestHandler[]} preMiddleware Middleware mounted before everything
170
+ * @property {import('express').RequestHandler[]} postMiddleware Middleware mounted after everything
171
+ * @property {import('express').RequestHandler[]} csrfMiddleware CSRF get/set form middleware
172
+ * @property {import('express').RequestHandler} sessionMiddleware Session middleware
173
+ * @property {import('express').RequestHandler[]} cookieParserMiddleware Cookie-parsing middleware
174
+ * @property {import('express').RequestHandler[]} i18nMiddleware I18n preparation middleware
175
+ * @property {import('express').RequestHandler} bodyParserMiddleware Body parsing middleware
176
+ * @property {Mounter} mount Function used to mount all CASA artifacts onto an ExpressJS app
177
+ * @property {ConfigurationOptions} config Ingested config supplied to `configure()`
178
+ */
179
+
180
+ /**
181
+ * Configuration for generating a ValidationError.
182
+ * i.e. `new ValidationError(configObject)`
183
+ * <br/><br/>
184
+ *
185
+ * The `fieldKeySuffix` is used to differentiate errors attached to
186
+ * the same field name. For example, given these fields inputs ...
187
+ *
188
+ * <pre>
189
+ * &lt;input name="dateOfBirth[dd]" /&gt;
190
+ * &lt;input name="dateOfBirth[mm]" /&gt;
191
+ * &lt;input name="dateOfBirth[yyyy]" /&gt;
192
+ * </pre>
193
+ *
194
+ * If we wanted to generate an error specifically for the `dd`
195
+ * element, then we'd include `{ fieldKeySuffix: '[dd]' }` in this
196
+ * config.
197
+ * <br/><br/>
198
+ *
199
+ * We can also use `focusSuffix` to control which properties of an
200
+ * object field should be highlighted with a red border when in error. Looking
201
+ * again at the `dateOfBirth` example above, if we did not specify
202
+ * any `focusSuffix`, then all three inputs would be highlighted.
203
+ * However, if we use `{ focusSuffix: ['[dd]', '[yyyy]'] }` then only
204
+ * the `[dd]` and `[yyyy]` inputs would be highlighted.
205
+ * <br/><br/>
206
+ *
207
+ * The `fieldHref` and `field` properties are strictly for
208
+ * internal use only and public access may be removed at any point.
209
+ *
210
+ * @typedef {object} ErrorMessageConfigObject
211
+ * @property {string} summary Summary message
212
+ * @property {string} [inline] Inline message (@deprecated now uses summary everywhere)
213
+ * @property {string|string[]} [focusSuffix] String(s) to append to URL hash for focusing inputs
214
+ * @property {string} [fieldKeySuffix] Object fields may use this to show errors per sub-property
215
+ * @property {object|ErrorMessageVariablesGenerator} [variables] Interpolation variables
216
+ * @property {string} [validator] Name of the validator
217
+ * @property {string} [fieldHref] (internal) URL hash to link to field in UI, i.e `#f-..`
218
+ * @property {string} [field] (internal) Field name, including any focus suffix
219
+ */
220
+
221
+ /**
222
+ * Function to generate interpolation variables for injecting into the error
223
+ * message string.
224
+ *
225
+ * @callback ErrorMessageVariablesGenerator
226
+ * @param {ValidateContext} dataContext Data context
227
+ * @returns {object} Variables name:value hash
228
+ */
229
+
230
+ /**
231
+ * @callback ErrorMessageConfigGenerator
232
+ * @param {ValidateContext} dataContext Data context
233
+ * @returns {string|ErrorMessageConfigObject} Compiled error message config
234
+ */
235
+
236
+ /**
237
+ * @typedef {string|ErrorMessageConfigObject|ErrorMessageConfigGenerator|Error} ErrorMessageConfig
238
+ */
239
+
240
+ /**
241
+ * @typedef {object} ValidateContext Context passed to validate function
242
+ * @property {JourneyContext} journeyContext Journey context
243
+ * @property {string} waypoint Waypoint
244
+ * @property {string} fieldName Name of field being processed
245
+ * @property {any} [fieldValue] Current value of the field being validated
246
+ * @property {string} [validator] Name of the validator
247
+ */
248
+
249
+ /**
250
+ * @callback ValidateFunction
251
+ * @param {any} value
252
+ * @param {ValidateContext} context Vaildation context
253
+ * @returns {ValidationError[]}
254
+ */
255
+
256
+ /**
257
+ * @callback FieldProcessorFunction
258
+ * @param {any} value Value to be processed
259
+ * @returns {any}
260
+ */
261
+
262
+ /**
263
+ * @typedef {object} Validator
264
+ * @property {ValidateFunction} validate Validation function
265
+ * @property {FieldProcessorFunction} sanitise Sanitise a given value prior to validation
266
+ * @property {object} config Configuration
267
+ * @property {string} name Validator name
268
+ */
269
+
270
+ /**
271
+ * @typedef {object} ValidatorConditionFunctionParams
272
+ * @property {string} fieldName Field name
273
+ * @property {any} fieldValue Field value
274
+ * @property {string} waypoint Waypoint
275
+ * @property {string} waypointId [DEPRECATED] Waypoint (for backwards compatibility with v7)
276
+ * @property {JourneyContext} journeyContext Journey Context
277
+ */
278
+
279
+ /**
280
+ * Condition functions are executed unbound.
281
+ *
282
+ * @callback ValidatorConditionFunction
283
+ * @param {ValidatorConditionFunctionParams} context Value to be processed
284
+ * @returns {boolean} True if the validators should be run
285
+ */
286
+
287
+ /**
288
+ * @typedef {object} PlanRoute
289
+ * @property {string} source Source waypoint
290
+ * @property {string} target Target waypoint
291
+ * @property {string} name Name
292
+ * @property {string} label Label
293
+ */
294
+
295
+ /**
296
+ * @callback PlanRouteCondition
297
+ * @param {PlanRoute} route Route metadata
298
+ * @param {JourneyContext} context Journey Context
299
+ * @returns {boolean} Returns true is route should be followed
300
+ */
301
+
302
+ /**
303
+ * @typedef PlanTraverseOptions
304
+ * @property {string} [startWaypoint] Waypoint from which to start (defaults to first in list)
305
+ * @property {string} routeName Follow routes matching this name (next | prev)
306
+ * @property {Map} history Used to detect loops in traversal (INTERNAL USE ONLY)
307
+ * @property {Function} [stopCondition] If true, traversal will be stopped (useful for performance)
308
+ * @property {string|PlanArbiter} [arbiter] Multiple target routes found, this decides which to use
309
+ */
310
+
311
+ /**
312
+ * @typedef {object} PlanArbiterParams
313
+ * @property {PlanRoute[]} targets Potential target routes that need arbitration
314
+ * @property {JourneyContext} journeyContext Journey Context
315
+ * @property {PlanTraverseOptions} traverseOptions Original traverse options passed to `traverse()`
316
+ */
317
+
318
+ /**
319
+ * @callback PlanArbiter
320
+ * @param {PlanArbiterParams} route Route metadata
321
+ * @returns {PlanRoute[]} Returns all routes, excluding those that the arbiter could eliminate
322
+ */
323
+
324
+ /**
325
+ * @typedef {object} JourneyContextObject Journey Context Object
326
+ * @property {Record<string,any>} [data] Data
327
+ * @property {any} [validation] Validation state
328
+ * @property {any} [nav] Navigation meta
329
+ * @property {any} [identity] Identity meta
330
+ */
@@ -0,0 +1,104 @@
1
+ import { FileSystemLoader } from 'nunjucks';
2
+
3
+ /**
4
+ * @access private
5
+ * @typedef {import('nunjucks').FileSystemLoaderOptions} FileSystemLoaderOptions
6
+ */
7
+
8
+ /**
9
+ * @access private
10
+ * @typedef {import('nunjucks').LoaderSource} LoaderSource
11
+ */
12
+
13
+ const VALID_BLOCKS = [
14
+ 'beforeContent',
15
+ 'bodyEnd',
16
+ 'bodyStart',
17
+ 'casaPageTitle',
18
+ 'content',
19
+ 'footer',
20
+ 'head',
21
+ 'header',
22
+ 'headIcons',
23
+ 'journey_form',
24
+ 'main',
25
+ 'pageTitle',
26
+ 'skipLink',
27
+ ];
28
+
29
+ /**
30
+ * @callback BlockModifier
31
+ * @param {string} templateName Path to the template being modified
32
+ * @returns {string} The modified template source
33
+ */
34
+
35
+ /**
36
+ * @access private
37
+ * @augments FileSystemLoader
38
+ */
39
+ export default class CasaTemplateLoader extends FileSystemLoader {
40
+ #blockModifiers;
41
+
42
+ /**
43
+ * Constructor.
44
+ *
45
+ * @param {string[]} searchPaths Template directories
46
+ * @param {FileSystemLoaderOptions} opts Loader options
47
+ */
48
+ constructor(searchPaths, opts) {
49
+ super(searchPaths, opts);
50
+
51
+ this.#blockModifiers = [];
52
+ }
53
+
54
+ /**
55
+ * Extract the source from the given template file.
56
+ *
57
+ * @param {string} name Source file path
58
+ * @returns {LoaderSource} Source contents of template
59
+ */
60
+ getSource(name) {
61
+ const source = super.getSource(name);
62
+ return source ? this.#applyBlockModifiers(name, source) : source;
63
+ }
64
+
65
+ /**
66
+ * Add a modification function to the loader.
67
+ *
68
+ * @param {string} block Block name, e.g. `bodyStart`
69
+ * @param {BlockModifier} modifier Modifier function
70
+ * @returns {void}
71
+ * @throws {Error} If provided with an unrecognised block
72
+ */
73
+ modifyBlock(block, modifier) {
74
+ // Limit to only known block so the user can't do general string replacements
75
+ if (!VALID_BLOCKS.includes(block)) {
76
+ throw new Error(`Block "${String(block)}" is not a recognised template block.`);
77
+ }
78
+
79
+ this.#blockModifiers.push({
80
+ block,
81
+ modifier,
82
+ });
83
+ }
84
+
85
+ /**
86
+ * Apply a block modifier to the given source content.
87
+ *
88
+ * @param {string} name Block name
89
+ * @param {string} source Original source pulled from template file
90
+ * @returns {string} The modified source
91
+ */
92
+ #applyBlockModifiers(name, source) {
93
+ for (let i = 0, l = this.#blockModifiers.length; i < l; i++) {
94
+ // ESLint disabled as `i` is an integer
95
+ /* eslint-disable-next-line security/detect-object-injection */
96
+ const { block, modifier } = this.#blockModifiers[i];
97
+ if (source.src.indexOf(`block ${block}`) > -1) {
98
+ /* eslint-disable-next-line no-param-reassign */
99
+ source.src = source.src.replace(`block ${block} %}`, `block ${block} %}${modifier(name)}`);
100
+ }
101
+ }
102
+ return source;
103
+ }
104
+ }