Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make components use formatErrorMessage for error messages #5358

Merged
merged 3 commits into from
Oct 4, 2024

Conversation

romaricpascal
Copy link
Member

@romaricpascal romaricpascal commented Sep 27, 2024

The helper was introduced by #5334 to format the ElementError message based on the component's moduleName. For consistency, other errors should follow the same format.

Components throw either:

  • SupportError whose message is not attached to a CharacterCount
  • ElementError whose message is already computed appropriately
  • ConfigError when the Character Count's config is not OK
  • InitError when already initialised

SupportError having no component name and ElementError having been dealt with, this PR updates ConfigError and InitError appropriately.

Thoughts

The I18n class also throws errors prefixed by i18n: ... in a similar format as we do for the components. It'd be nice to find a way to harmonise the error formatting for it. Giving it a moduleName would muddy a bit the meaning of moduleName, which I think we should keep for the components. We could make formatErrorMessage accept an arbitrary string as prefix or a function that handles the formatting (and would avoid it requiring a component with a moduleName property) 🤔 That'd need more exploration and we can look at this in a future piece of work focused on error handling.

Closes #5325

Copy link

github-actions bot commented Sep 27, 2024

📋 Stats

File sizes

File Size
dist/govuk-frontend-development.min.css 118.62 KiB
dist/govuk-frontend-development.min.js 44.45 KiB
packages/govuk-frontend/dist/govuk/all.bundle.js 93.86 KiB
packages/govuk-frontend/dist/govuk/all.bundle.mjs 88.11 KiB
packages/govuk-frontend/dist/govuk/all.mjs 1.18 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs 1.19 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend.min.css 118.6 KiB
packages/govuk-frontend/dist/govuk/govuk-frontend.min.js 44.44 KiB
packages/govuk-frontend/dist/govuk/i18n.mjs 5.55 KiB
packages/govuk-frontend/dist/govuk/init.mjs 6.9 KiB

Modules

File Size (bundled) Size (minified)
all.mjs 84.01 KiB 41.67 KiB
accordion.mjs 24.92 KiB 12.83 KiB
button.mjs 7.42 KiB 3.21 KiB
character-count.mjs 23.84 KiB 10.38 KiB
checkboxes.mjs 7.27 KiB 3.32 KiB
error-summary.mjs 9.33 KiB 3.97 KiB
exit-this-page.mjs 18.54 KiB 9.75 KiB
header.mjs 5.9 KiB 3.09 KiB
notification-banner.mjs 7.71 KiB 3.13 KiB
password-input.mjs 16.57 KiB 7.73 KiB
radios.mjs 6.27 KiB 2.88 KiB
service-navigation.mjs 5.88 KiB 3.13 KiB
skip-link.mjs 5.82 KiB 2.68 KiB
tabs.mjs 11.47 KiB 6.54 KiB

View stats and visualisations on the review app


Action run for 1734acb

Copy link

github-actions bot commented Sep 27, 2024

JavaScript changes to npm package

diff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
index 4cb3bbdca..9df9ea7e6 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend.min.js
@@ -80,6 +80,10 @@ function isObject(t) {
     }(t)
 }
 
+function formatErrorMessage(Component, t) {
+    return `${Component.moduleName}: ${t}`
+}
+
 function normaliseDataset(Component, t) {
     const e = {};
     for (const [n, i] of Object.entries(Component.schema.properties)) n in t && (e[n] = normaliseString(t[n], i)), "object" === (null == i ? void 0 : i.type) && (e[n] = extractConfigByNamespace(Component, t, n));
@@ -111,32 +115,30 @@ class ElementError extends GOVUKFrontendError {
                 element: o,
                 expectedType: s
             } = t;
-            e = i, e += o ? ` is not of type ${null!=s?s:"HTMLElement"}` : " not found", e = function(Component, t) {
-                return `${Component.moduleName}: ${t}`
-            }(n, e)
+            e = i, e += o ? ` is not of type ${null!=s?s:"HTMLElement"}` : " not found", e = formatErrorMessage(n, e)
         }
         super(e), this.name = "ElementError"
     }
 }
 class InitError extends GOVUKFrontendError {
-    constructor(t, e) {
-        let n = `moduleName not defined in component (\`${e}\`)`;
-        "string" == typeof t && (n = `Root element (\`$root\`) already initialised (\`${t}\`)`), super(n), this.name = "InitError"
+    constructor(t) {
+        super("string" == typeof t ? t : formatErrorMessage(t, "Root element (`$root`) already initialised")), this.name = "InitError"
     }
 }
 class GOVUKFrontendComponent {
     constructor(t) {
         const e = this.constructor;
+        if ("string" != typeof e.moduleName) throw new InitError("`moduleName` not defined in component");
         e.checkSupport(), this.checkInitialised(t);
         const n = e.moduleName;
-        if ("string" != typeof n) throw new InitError(n);
-        n && (null == t || t.setAttribute(`data-${n}-init`, ""))
+        null == t || t.setAttribute(`data-${n}-init`, "")
     }
     checkInitialised(t) {
-        const e = this.constructor.moduleName;
-        if (t && e && function(t, e) {
+        const e = this.constructor,
+            n = e.moduleName;
+        if (t && n && function(t, e) {
                 return t instanceof HTMLElement && t.hasAttribute(`data-${e}-init`)
-            }(t, e)) throw new InitError(e)
+            }(t, n)) throw new InitError(e)
     }
     static checkSupport() {
         if (!isSupported()) throw new SupportError
@@ -446,7 +448,7 @@ class CharacterCount extends GOVUKFrontendComponent {
             }
             return n
         }(CharacterCount.schema, this.config);
-        if (a[0]) throw new ConfigError(`Character count: ${a[0]}`);
+        if (a[0]) throw new ConfigError(formatErrorMessage(CharacterCount, a[0]));
         this.i18n = new I18n(this.config.i18n, {
             locale: closestAttributeValue(t, "lang")
         }), this.maxLength = null != (n = null != (i = this.config.maxwords) ? i : this.config.maxlength) ? n : 1 / 0, this.$root = t, this.$textarea = o;

Action run for 1734acb

Copy link

github-actions bot commented Sep 27, 2024

Other changes to npm package

diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.js b/packages/govuk-frontend/dist/govuk/all.bundle.js
index 92f1e5508..73aa0d036 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.js
@@ -246,32 +246,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -283,7 +283,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
@@ -971,7 +971,7 @@
       this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
       const errors = validateConfig(CharacterCount.schema, this.config);
       if (errors[0]) {
-        throw new ConfigError(`Character count: ${errors[0]}`);
+        throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
       }
       this.i18n = new I18n(this.config.i18n, {
         locale: closestAttributeValue($root, 'lang')
diff --git a/packages/govuk-frontend/dist/govuk/all.bundle.mjs b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
index 4be703384..6327913b4 100644
--- a/packages/govuk-frontend/dist/govuk/all.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/all.bundle.mjs
@@ -240,32 +240,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -277,7 +277,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
@@ -965,7 +965,7 @@ class CharacterCount extends GOVUKFrontendComponent {
     this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
     const errors = validateConfig(CharacterCount.schema, this.config);
     if (errors[0]) {
-      throw new ConfigError(`Character count: ${errors[0]}`);
+      throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
     }
     this.i18n = new I18n(this.config.i18n, {
       locale: closestAttributeValue($root, 'lang')
diff --git a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
index f4a246aab..b00a99a02 100644
--- a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js
@@ -180,32 +180,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -217,7 +217,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
index 213b46e03..4d348f991 100644
--- a/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs
@@ -174,32 +174,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -211,7 +211,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/button/button.bundle.js b/packages/govuk-frontend/dist/govuk/components/button/button.bundle.js
index dbdd324a1..b275dda25 100644
--- a/packages/govuk-frontend/dist/govuk/components/button/button.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/button/button.bundle.js
@@ -180,32 +180,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -217,7 +217,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/button/button.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/button/button.bundle.mjs
index 669d9ade3..ba3248405 100644
--- a/packages/govuk-frontend/dist/govuk/components/button/button.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/button/button.bundle.mjs
@@ -174,32 +174,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -211,7 +211,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js
index 58d01c6d1..6012a2e72 100644
--- a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js
@@ -211,32 +211,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -248,7 +248,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
@@ -505,7 +505,7 @@
       this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
       const errors = validateConfig(CharacterCount.schema, this.config);
       if (errors[0]) {
-        throw new ConfigError(`Character count: ${errors[0]}`);
+        throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
       }
       this.i18n = new I18n(this.config.i18n, {
         locale: closestAttributeValue($root, 'lang')
diff --git a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs
index b5f91ce11..b4df99f57 100644
--- a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs
@@ -205,32 +205,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -242,7 +242,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
@@ -499,7 +499,7 @@ class CharacterCount extends GOVUKFrontendComponent {
     this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
     const errors = validateConfig(CharacterCount.schema, this.config);
     if (errors[0]) {
-      throw new ConfigError(`Character count: ${errors[0]}`);
+      throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
     }
     this.i18n = new I18n(this.config.i18n, {
       locale: closestAttributeValue($root, 'lang')
diff --git a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.mjs b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.mjs
index d121340f8..af34aa19c 100644
--- a/packages/govuk-frontend/dist/govuk/components/character-count/character-count.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/character-count/character-count.mjs
@@ -1,5 +1,5 @@
 import { closestAttributeValue } from '../../common/closest-attribute-value.mjs';
-import { mergeConfigs, validateConfig } from '../../common/index.mjs';
+import { mergeConfigs, validateConfig, formatErrorMessage } from '../../common/index.mjs';
 import { normaliseDataset } from '../../common/normalise-dataset.mjs';
 import { ElementError, ConfigError } from '../../errors/index.mjs';
 import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
@@ -62,7 +62,7 @@ class CharacterCount extends GOVUKFrontendComponent {
     this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
     const errors = validateConfig(CharacterCount.schema, this.config);
     if (errors[0]) {
-      throw new ConfigError(`Character count: ${errors[0]}`);
+      throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
     }
     this.i18n = new I18n(this.config.i18n, {
       locale: closestAttributeValue($root, 'lang')
diff --git a/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js b/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js
index 29e6f3a6e..c5d11670d 100644
--- a/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js
@@ -91,32 +91,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -128,7 +128,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs
index e61909ab8..db0837d00 100644
--- a/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs
@@ -85,32 +85,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -122,7 +122,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js b/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js
index c4cc5c503..dd6d7f516 100644
--- a/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js
@@ -210,32 +210,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -247,7 +247,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs
index dc4643b4e..d5cd760d9 100644
--- a/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs
@@ -204,32 +204,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -241,7 +241,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js b/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js
index aa4cf803f..2c62842b7 100644
--- a/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js
@@ -180,32 +180,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -217,7 +217,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs
index 28c4f973e..d101649ae 100644
--- a/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs
@@ -174,32 +174,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -211,7 +211,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/header/header.bundle.js b/packages/govuk-frontend/dist/govuk/components/header/header.bundle.js
index 9f4d5db7d..87387c539 100644
--- a/packages/govuk-frontend/dist/govuk/components/header/header.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/header/header.bundle.js
@@ -99,32 +99,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -136,7 +136,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/header/header.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/header/header.bundle.mjs
index f78ae39f5..1107d1c8d 100644
--- a/packages/govuk-frontend/dist/govuk/components/header/header.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/header/header.bundle.mjs
@@ -93,32 +93,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -130,7 +130,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js b/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js
index 4d4a14c1e..6e8d9590b 100644
--- a/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js
@@ -204,32 +204,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -241,7 +241,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs
index 0e5cc4ced..d276a5b33 100644
--- a/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs
@@ -198,32 +198,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -235,7 +235,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js b/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js
index b1e98bc5e..8b54e720e 100644
--- a/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js
@@ -185,32 +185,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -222,7 +222,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs
index 58050dae9..c9645cd58 100644
--- a/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs
@@ -179,32 +179,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -216,7 +216,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.js b/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.js
index 1c1a4b8fd..40ea3fa82 100644
--- a/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.js
@@ -91,32 +91,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -128,7 +128,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs
index c2be5ebd6..71b576f0e 100644
--- a/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs
@@ -85,32 +85,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -122,7 +122,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js b/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js
index 648cb215e..199dfe565 100644
--- a/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js
@@ -99,32 +99,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -136,7 +136,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs
index e7ac63283..d524d126e 100644
--- a/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs
@@ -93,32 +93,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -130,7 +130,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js b/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js
index 82665fab1..0dc097245 100644
--- a/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js
@@ -121,32 +121,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -158,7 +158,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs
index d62b18e92..3a7efc940 100644
--- a/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs
@@ -115,32 +115,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -152,7 +152,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js b/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js
index 030d1168d..e636ee5f6 100644
--- a/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js
+++ b/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js
@@ -105,32 +105,32 @@
     }
   }
   class InitError extends GOVUKFrontendError {
-    constructor(moduleName, className) {
-      let errorText = `moduleName not defined in component (\`${className}\`)`;
-      if (typeof moduleName === 'string') {
-        errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-      }
-      super(errorText);
+    constructor(componentOrMessage) {
+      const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+      super(message);
       this.name = 'InitError';
     }
   }
+  /**
+   * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+   */
 
   class GOVUKFrontendComponent {
     constructor($root) {
       const childConstructor = this.constructor;
+      if (typeof childConstructor.moduleName !== 'string') {
+        throw new InitError(`\`moduleName\` not defined in component`);
+      }
       childConstructor.checkSupport();
       this.checkInitialised($root);
       const moduleName = childConstructor.moduleName;
-      if (typeof moduleName === 'string') {
-        moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-      } else {
-        throw new InitError(moduleName);
-      }
+      $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
     }
     checkInitialised($root) {
-      const moduleName = this.constructor.moduleName;
+      const constructor = this.constructor;
+      const moduleName = constructor.moduleName;
       if ($root && moduleName && isInitialised($root, moduleName)) {
-        throw new InitError(moduleName);
+        throw new InitError(constructor);
       }
     }
     static checkSupport() {
@@ -142,7 +142,7 @@
 
   /**
    * @typedef ChildClass
-   * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+   * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
    */
 
   /**
diff --git a/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs b/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs
index 5f7536551..238bc76ac 100644
--- a/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs
+++ b/packages/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs
@@ -99,32 +99,32 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -136,7 +136,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**
diff --git a/packages/govuk-frontend/dist/govuk/errors/index.mjs b/packages/govuk-frontend/dist/govuk/errors/index.mjs
index 50c0f8ffe..2e336a82a 100644
--- a/packages/govuk-frontend/dist/govuk/errors/index.mjs
+++ b/packages/govuk-frontend/dist/govuk/errors/index.mjs
@@ -43,15 +43,15 @@ class ElementError extends GOVUKFrontendError {
   }
 }
 class InitError extends GOVUKFrontendError {
-  constructor(moduleName, className) {
-    let errorText = `moduleName not defined in component (\`${className}\`)`;
-    if (typeof moduleName === 'string') {
-      errorText = `Root element (\`$root\`) already initialised (\`${moduleName}\`)`;
-    }
-    super(errorText);
+  constructor(componentOrMessage) {
+    const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
+    super(message);
     this.name = 'InitError';
   }
 }
+/**
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
+ */
 
 export { ConfigError, ElementError, GOVUKFrontendError, InitError, SupportError };
 //# sourceMappingURL=index.mjs.map
diff --git a/packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs b/packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs
index a9075fde8..2e5786db9 100644
--- a/packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs
+++ b/packages/govuk-frontend/dist/govuk/govuk-frontend-component.mjs
@@ -4,19 +4,19 @@ import { InitError, SupportError } from './errors/index.mjs';
 class GOVUKFrontendComponent {
   constructor($root) {
     const childConstructor = this.constructor;
+    if (typeof childConstructor.moduleName !== 'string') {
+      throw new InitError(`\`moduleName\` not defined in component`);
+    }
     childConstructor.checkSupport();
     this.checkInitialised($root);
     const moduleName = childConstructor.moduleName;
-    if (typeof moduleName === 'string') {
-      moduleName && ($root == null ? void 0 : $root.setAttribute(`data-${moduleName}-init`, ''));
-    } else {
-      throw new InitError(moduleName);
-    }
+    $root == null || $root.setAttribute(`data-${moduleName}-init`, '');
   }
   checkInitialised($root) {
-    const moduleName = this.constructor.moduleName;
+    const constructor = this.constructor;
+    const moduleName = constructor.moduleName;
     if ($root && moduleName && isInitialised($root, moduleName)) {
-      throw new InitError(moduleName);
+      throw new InitError(constructor);
     }
   }
   static checkSupport() {
@@ -28,7 +28,7 @@ class GOVUKFrontendComponent {
 
 /**
  * @typedef ChildClass
- * @property {string} [moduleName] - The module name that'll be looked for in the DOM when initialising the component
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
  */
 
 /**

Action run for 1734acb

It aligns the `ConfigError` it throws with the rest of the errors thrown by components
…c property

This allows to make the `moduleName` in `ChildClass` non-optional,
which will be required for using `formatErrorMessage`, which requires a `ComponentWithModuleName`
…rbitrary message

This ensures errors thrown when the component is already initialised have the same format
as other errors related to components. Accepting an arbitrary message was the simplest way
I figured to type the arguments of `InitError` (and I think should be a default for all errors)

Tests for each component need updating as the messages have changed
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-5358 September 30, 2024 11:17 Inactive
@romaricpascal romaricpascal marked this pull request as ready for review September 30, 2024 14:10
Copy link
Contributor

@patrickpatrickpatrick patrickpatrickpatrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to extrapolate the helper to all errors that need it. Looks good to me.

@romaricpascal romaricpascal merged commit 1107ab3 into public-js-api Oct 4, 2024
50 checks passed
@romaricpascal romaricpascal deleted the use-formaterrormessage branch October 4, 2024 09:44
@romaricpascal romaricpascal linked an issue Oct 7, 2024 that may be closed by this pull request
2 tasks
patrickpatrickpatrick pushed a commit that referenced this pull request Oct 7, 2024
Make components use `formatErrorMessage` for error messages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor errors prefixed by the component name to use formatErrorMessage
3 participants