@port60/template-kit 0.18.0 → 0.20.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.
@@ -28,6 +28,19 @@ const Ajv = Ajv2020.default ?? Ajv2020;
28
28
 
29
29
  export { dialect as contractDialect, sectionCatalogue, islandRegistry, contextContract, behaviourCatalogue };
30
30
 
31
+ // The behaviour to opt-in-attribute map is DERIVED from the catalogue (FR-5): `primaryAttribute` on
32
+ // each entry feeds the checks below, the generated reference and the kit, so adding a behaviour is
33
+ // one catalogue entry plus its runtime initialiser. The trailing word boundary keeps the original
34
+ // semantics: a companion attribute that starts with the primary one (data-p60-reveal-group,
35
+ // data-p60-nav-item) counts as the behaviour in use.
36
+ const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
37
+ export const BEHAVIOUR_PRIMARY_ATTR = Object.fromEntries(
38
+ behaviourCatalogue.behaviours.map((b) => [b.name, b.primaryAttribute])
39
+ );
40
+ const PRIMARY_ATTR = Object.fromEntries(
41
+ behaviourCatalogue.behaviours.map((b) => [b.name, [new RegExp(`${escapeRegExp(b.primaryAttribute)}\\b`), b.primaryAttribute]])
42
+ );
43
+
31
44
  // Templates are markup and attributes, NEVER code (docs/template-behaviours.md). These are hard
32
45
  // errors over the RAW liquid source — even inside comments, because there is no legitimate reason
33
46
  // for the tokens to appear at all. The handler pattern names real DOM event families rather than
@@ -144,19 +157,6 @@ export async function validateArtifact(files) {
144
157
  .filter(([path]) => path.endsWith('.liquid'))
145
158
  .map(([, source]) => source)
146
159
  .join('\n');
147
- const PRIMARY_ATTR = {
148
- reveal: [/data-p60-reveal\b/, 'data-p60-reveal'],
149
- counter: [/data-p60-count\b/, 'data-p60-count'],
150
- progress: [/data-p60-progress\b/, 'data-p60-progress'],
151
- countdown: [/data-p60-countdown\b/, 'data-p60-countdown'],
152
- accordion: [/data-p60-accordion\b/, 'data-p60-accordion'],
153
- carousel: [/data-p60-carousel\b/, 'data-p60-carousel'],
154
- stickyHeader: [/data-p60-sticky-header\b/, 'data-p60-sticky-header'],
155
- stickyCta: [/data-p60-sticky-cta\b/, 'data-p60-sticky-cta'],
156
- lightbox: [/data-p60-lightbox\b/, 'data-p60-lightbox'],
157
- tabs: [/data-p60-tabs\b/, 'data-p60-tabs'],
158
- nav: [/data-p60-nav\b/, 'data-p60-nav'],
159
- };
160
160
  for (const name of declaredBehaviours) {
161
161
  const primary = PRIMARY_ATTR[name];
162
162
  if (primary && !primary[0].test(liquidSource)) {
@@ -185,6 +185,16 @@ export async function validateArtifact(files) {
185
185
  if (/data-p60-nav-(item|burger)\b/.test(liquidSource) && !/data-p60-nav(?!-)\b/.test(liquidSource)) {
186
186
  errors.push("behaviour 'nav': data-p60-nav-item and data-p60-nav-burger need a data-p60-nav root around the menu");
187
187
  }
188
+ if (/data-p60-show-more(?!-)\b/.test(liquidSource)
189
+ && (!/data-p60-show-more-item\b/.test(liquidSource) || !/data-p60-show-more-toggle\b/.test(liquidSource))) {
190
+ errors.push("behaviour 'showMore': a data-p60-show-more list needs data-p60-show-more-item entries and a data-p60-show-more-toggle button");
191
+ }
192
+ if (/data-p60-show-more-toggle\b/.test(liquidSource) && !/<button\b[^>]*data-p60-show-more-toggle\b/.test(liquidSource)) {
193
+ errors.push("behaviour 'showMore': data-p60-show-more-toggle belongs on a <button> (a link would navigate; a div would not be operable)");
194
+ }
195
+ if (/data-p60-scrollspy\b/.test(liquidSource) && !/href=["']#[^"'\s]/.test(liquidSource)) {
196
+ errors.push("behaviour 'scrollspy': a data-p60-scrollspy menu needs links to in-page anchors (href=\"#section-id\")");
197
+ }
188
198
  }
189
199
 
190
200
  // Font knobs: the default family must be a real catalogue entry (the tenant-unset render uses