// Updated cat.js with Blob URL Integration and Bundling Parameter

(function() {
  'use strict';

  /**
   * Global configuration object for Cat.js
   * Developers can set CatConfig.bundleAll before loading components.
   */
  window.CatConfig = window.CatConfig || {
      bundleAll: false // Default: Per-Component Blob Files
  };

  /**
   * Internal module for tracking loaded files.
   */
  const Registry = (function() {
      const loadedFiles = new Set();

      return {
          /**
           * Checks if a file has already been loaded.
           * @param {string} path - The path to the file.
           * @returns {boolean} - True if the file is loaded, else false.
           */
          isFileLoaded: (path) => loadedFiles.has(path),

          /**
           * Marks a file as loaded to prevent redundant loading.
           * @param {string} path - The path to the file.
           */
          markFileAsLoaded: (path) => loadedFiles.add(path),
      };
  })();

  /**
   * Compiler module for generating and registering component classes using Blob URLs.
   */
  const Compiler = (function() {
      // Array to store compiled component codes in bundling mode
      let bundledCode = '';
      // Flag to indicate if bundling script has been injected
      let bundleScriptInjected = false;

      /**
       * Compiles a single component's template and script into a class definition.
       * @param {string} name - The name of the component (e.g., 'my-component').
       * @param {string} templateContent - The inner HTML of the component's <template>.
       * @param {string} scriptContent - The content of the component's <script>.
       * @returns {string} - The compiled class code as a string.
       */
      function compileComponentCode(name, templateContent, scriptContent) {
          // Helper functions
          function componentNameToClassName(name) {
              return name.split('-').map(s => s[0].toUpperCase() + s.slice(1)).join('');
          }
          function extractDynamicExpressions(text) {
              const regex = /\{([^}]+)\}/g;
              let match;
              const expressions = [];
              while ((match = regex.exec(text)) !== null) {
                  expressions.push(match[1].trim());
              }
              return expressions;
          }
          function replaceExpressions(text) {
              return text.replace(/\{([^}]+)\}/g, '${this.$1}');
          }

          const className = componentNameToClassName(name);

          // Create a template element and set its innerHTML to the templateContent
          const templateElement = document.createElement('template');
          templateElement.innerHTML = templateContent;
          const templateContentFragment = templateElement.content;

          // Initialize code snippets
          let elementCode = '';
          let propertyInitializations = '';
          let getterSetterCode = '';

          const dynamicProperties = new Set();
          const propertyToUpdateCode = {};
          const initialProperties = {};
          const propertiesHandled = new Set();

          let nodeCounter = 1;

          /**
           * Processes a DOM node to generate corresponding JavaScript code.
           * @param {Node} node - The DOM node to process.
           * @param {string} parentVarName - The variable name of the parent element in the generated code.
           */
          function processNode(node, parentVarName) {
              if (node.nodeType === Node.ELEMENT_NODE) {
                  const nodeName = node.tagName.toLowerCase();

                  if (nodeName === 'style') {
                      // Create code to create and append style element
                      const styleContent = node.textContent;
                      if (styleContent.trim()) {
                          elementCode += `const style = document.createElement('style');\n`;
                          elementCode += `style.textContent = ${JSON.stringify(styleContent)};\n`;
                          elementCode += `${parentVarName}.appendChild(style);\n`;
                      }
                      return;
                  }

                  // Assign instance variable name
                  let nodeVarName;
                  if (node.id) {
                      nodeVarName = `this.$${node.id}`;
                  } else if (['button', 'input', 'div', 'span', 'p', 'a'].includes(nodeName)) {
                      nodeVarName = `this._${nodeName}${nodeCounter++}`;
                  } else {
                      nodeVarName = `node${nodeCounter++}`;
                  }

                  // Create node
                  if (nodeVarName.startsWith('this.')) {
                      // Assign to this property without 'const'
                      elementCode += `${nodeVarName} = document.createElement('${nodeName}');\n`;
                  } else {
                      // Local variable with 'const'
                      elementCode += `const ${nodeVarName} = document.createElement('${nodeName}');\n`;
                  }

                  // Process attributes
                  for (let attr of node.attributes) {
                      const attrName = attr.name;
                      const attrValue = attr.value;
                      const dynamicExprs = extractDynamicExpressions(attrValue);

                      if (dynamicExprs.length > 0) {
                          dynamicExprs.forEach(expr => {
                              dynamicProperties.add(expr);
                              if (!propertyToUpdateCode[expr]) {
                                  propertyToUpdateCode[expr] = new Set();
                              }
                              const updateCode = `${nodeVarName}.setAttribute('${attrName}', \`${replaceExpressions(attrValue)}\`);`;
                              propertyToUpdateCode[expr].add(updateCode);
                          });

                          // Initialize attribute with current property values
                          elementCode += `${nodeVarName}.setAttribute('${attrName}', \`${replaceExpressions(attrValue)}\`);\n`;

                      } else {
                          // Static attribute
                          elementCode += `${nodeVarName}.setAttribute('${attrName}', ${JSON.stringify(attrValue)});\n`;
                      }
                  }

                  // Process child nodes
                  if (node.childNodes.length > 0) {
                      for (let child of node.childNodes) {
                          processNode(child, nodeVarName);
                      }
                  }

                  // Append to parent
                  elementCode += `${parentVarName}.appendChild(${nodeVarName});\n`;

              } else if (node.nodeType === Node.TEXT_NODE) {
                  // Handle text nodes directly under parentVarName
                  const textContent = node.nodeValue;
                  if (textContent.trim() !== '') {
                      const dynamicExprs = extractDynamicExpressions(textContent);

                      if (dynamicExprs.length > 0) {
                          const textNodeVar = `this._textNode${nodeCounter++}`;
                          if (!propertiesHandled.has(textNodeVar)) {
                              propertiesHandled.add(textNodeVar);
                              if (textNodeVar.startsWith('this.')) {
                                  elementCode += `${textNodeVar} = document.createTextNode(\`${replaceExpressions(textContent)}\`);\n`;
                              } else {
                                  elementCode += `const ${textNodeVar} = document.createTextNode(\`${replaceExpressions(textContent)}\`);\n`;
                              }
                          }
                          elementCode += `${parentVarName}.appendChild(${textNodeVar});\n`;

                          dynamicExprs.forEach(expr => {
                              dynamicProperties.add(expr);
                              if (!propertyToUpdateCode[expr]) {
                                  propertyToUpdateCode[expr] = new Set();
                              }
                              const updateCode = `${textNodeVar}.textContent = \`${replaceExpressions(textContent)}\`;`;
                              propertyToUpdateCode[expr].add(updateCode);
                          });
                      } else {
                          const textNodeVar = `document.createTextNode(${JSON.stringify(textContent)})`;
                          elementCode += `${parentVarName}.appendChild(${textNodeVar});\n`;
                      }
                  }
              }
          }

          // Start processing from templateContentFragment
          elementCode += `const shadowRoot = this.attachShadow({ mode: 'open' });\n`;

          // Process child nodes of the template content
          const rootVarName = 'shadowRoot';
          for (let child of templateContentFragment.childNodes) {
              processNode(child, rootVarName);
          }

          // Now process scriptContent to find initial property assignments and function definitions

          /**
           * Extracts function definitions and remaining code from the script content.
           * @param {string} scriptContent - The script content to parse.
           * @returns {object} - An object containing arrays of function definitions and remaining code.
           */
          function extractFunctionDefinitions(scriptContent) {
              const functionDefinitions = [];
              const nonFunctionLines = [];

              let lines = scriptContent.split('\n');
              let insideFunction = false;
              let currentFunctionLines = [];
              let braceLevel = 0;

              for (let line of lines) {
                  // Include the line in the function or non-function code
                  if (!insideFunction) {
                      if (line.trim().startsWith('function ')) {
                          insideFunction = true;
                          braceLevel = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
                          currentFunctionLines.push(line);
                      } else {
                          // Collect initial property assignments
                          const propInitMatch = line.trim().match(/^this\.(\w+)\s*=\s*(.*);$/);
                          if (propInitMatch) {
                              const propName = propInitMatch[1];
                              const propValue = propInitMatch[2];
                              initialProperties[propName] = propValue;
                          } else if (line.trim() !== '') {
                              nonFunctionLines.push(line);
                          }
                      }
                  } else {
                      currentFunctionLines.push(line);
                      braceLevel += (line.match(/\{/g) || []).length;
                      braceLevel -= (line.match(/\}/g) || []).length;
                      if (braceLevel === 0) {
                          insideFunction = false;
                          functionDefinitions.push(currentFunctionLines.join('\n'));
                          currentFunctionLines = [];
                      }
                  }
              }

              return {
                  functionDefinitions,
                  remainingCode: nonFunctionLines.join('\n')
              };
          }

          const { functionDefinitions, remainingCode } = extractFunctionDefinitions(scriptContent);

          // Collect function names
          const functionNames = [];
          let classMethodsCode = '';

          // Process each function definition
          for (let funcDef of functionDefinitions) {
              // Extract function name, parameters, body
              const funcMatch = /^function\s+(\w+)\s*\(([^)]*)\)\s*\{([\s\S]*?)\}\s*$/m.exec(funcDef.trim());
              if (funcMatch) {
                  const funcName = funcMatch[1];
                  const funcParams = funcMatch[2];
                  let funcBody = funcMatch[3];

                  // Replace 'component.' with 'this.'
                  funcBody = funcBody.replace(/\bcomponent\./g, 'this.');

                  // Add to class methods
                  classMethodsCode += `
      ${funcName}(${funcParams}) {
          ${funcBody}
      }
      `;

                  // Collect function names
                  functionNames.push(funcName);
              }
          }

          // Initialize properties not assigned in scriptContent
          dynamicProperties.forEach(prop => {
              if (!initialProperties.hasOwnProperty(prop)) {
                  initialProperties[prop] = 'null';
              }
          });

          // Build property initializations
          for (let [prop, value] of Object.entries(initialProperties)) {
              propertyInitializations += `this._${prop} = ${value};\n`;
          }

          // Build getters and setters with update code
          for (let prop of dynamicProperties) {
              let updateCode = '';
              if (propertyToUpdateCode[prop]) {
                  updateCode = Array.from(propertyToUpdateCode[prop]).join('\n');
              }
              getterSetterCode += `
      get ${prop}() {
          return this._${prop};
      }
      set ${prop}(value) {
          this._${prop} = value;
          ${updateCode}
      }
      `;
          }

          // Adjust constructor code to bind methods
          let constructorCode = remainingCode;

          // Prepend 'const functionName = this.functionName.bind(this);'
          let bindingCode = '';
          for (let funcName of functionNames) {
              bindingCode += `const ${funcName} = this.${funcName}.bind(this);\n`;
          }

          // Prepend binding code to constructor code
          constructorCode = bindingCode + constructorCode;

          // Generate the full class code
          const classCode = `
  class ${className} extends HTMLElement {
      constructor() {
          super();
          ${propertyInitializations}
          ${elementCode}
          ${constructorCode}
      }

      ${getterSetterCode}

      ${classMethodsCode}

  }
  customElements.define('${name}', ${className});
          `;

          return classCode;
      }

      /**
       * Compiles and registers multiple components in bundling mode.
       * @param {Array} components - Array of component class codes.
       */
      function compileAndRegisterBundle(components) {
          if (bundleScriptInjected) return;
          bundleScriptInjected = true;

          // Combine all component codes into one string
          bundledCode = components.join('\n');

          // Create a Blob from the bundled code
          const blob = new Blob([bundledCode], { type: 'application/javascript' });
          const blobURL = URL.createObjectURL(blob);

          // Create a script element
          const script = document.createElement('script');
          script.src = blobURL;
          script.type = 'module'; // Use module type for modern JavaScript features
          script.onload = () => {
              console.log(`All components loaded and registered as a bundle.`);
              // Revoke the Blob URL after the script has been loaded to free memory
              URL.revokeObjectURL(blobURL);
          };
          script.onerror = (error) => {
              console.error(`Error loading component bundle:`, error);
              // Revoke the Blob URL on error to free memory
              URL.revokeObjectURL(blobURL);
          };

          // Append the script to the document head
          document.head.appendChild(script);
      }

      /**
       * Compiles and registers a single component or accumulates for bundling based on configuration.
       * @param {string} name - The name of the component.
       * @param {string} templateContent - The template content.
       * @param {string} scriptContent - The script content.
       */
      function compileAndRegister(name, templateContent, scriptContent) {
          const classCode = compileComponentCode(name, templateContent, scriptContent);

          if (window.CatConfig.bundleAll) {
              // Bundling mode: accumulate component codes
              compiledComponents.push(classCode);
          } else {
              // Per-Component mode: compile and register immediately
              compileAndRegisterComponent(name, classCode);
          }
      }

      /**
       * Finalizes the bundling process by injecting the bundled script.
       * Should be called after all components have been loaded.
       */
      function finalizeBundle() {
          if (window.CatConfig.bundleAll && compiledComponents.length > 0) {
              compileAndRegisterBundle(compiledComponents);
              compiledComponents = []; // Reset after bundling
          }
      }

      /**
       * Compiles and registers the component using Blob URLs.
       * @param {string} name - The name of the component.
       * @param {string} code - The compiled JavaScript class code.
       */
      function compileAndRegisterComponent(name, code) {
          // Create a Blob from the code
          const blob = new Blob([code], { type: 'application/javascript' });
          const blobURL = URL.createObjectURL(blob);

          // Create a script element
          const script = document.createElement('script');
          script.src = blobURL;
          script.type = 'module'; // Use module type for modern JavaScript features
          script.onload = () => {
              console.log(`Component ${name} loaded and registered.`);
              // Revoke the Blob URL after the script has been loaded to free memory
              URL.revokeObjectURL(blobURL);
          };
          script.onerror = (error) => {
              console.error(`Error loading component ${name}:`, error);
              // Revoke the Blob URL on error to free memory
              URL.revokeObjectURL(blobURL);
          };

          // Append the script to the document head
          document.head.appendChild(script);
      }

      // Array to store compiled component codes in bundling mode
      let compiledComponents = [];

      return {
          /**
           * Compiles and registers a component based on bundling configuration.
           * @param {string} name - The name of the component.
           * @param {string} templateContent - The template content.
           * @param {string} scriptContent - The script content.
           */
          compileComponent: compileAndRegister,

          /**
           * Finalizes the bundling process by injecting the bundled script.
           * Should be called when all components have been loaded.
           */
          finalizeBundle: finalizeBundle,
      };
  })();

  /**
   * Internal module for loading and parsing HTML files.
   */
  const Loader = (function() {
      /**
       * Loads an HTML file from the given path.
       * @param {string} path - The path to the HTML file (without .html extension).
       * @returns {Promise<string>} - A promise that resolves to the content of the HTML file.
       */
      async function loadFile(path) {
          const fullPath = `${path}.html`;

          try {
              const response = await fetch(fullPath);
              if (!response.ok) {
                  throw new Error(`Failed to load: ${fullPath} (Status: ${response.status})`);
              }
              const content = await response.text();
              return content;
          } catch (error) {
              console.error(error.message);
              throw error;
          }
      }

      /**
       * Parses an HTML string into a Document object.
       * @param {string} htmlString - The HTML string to parse.
       * @returns {Document} - The parsed Document object.
       */
      function parseHTML(htmlString) {
          const parser = new DOMParser();
          const doc = parser.parseFromString(htmlString, 'text/html');
          return doc;
      }

      return {
          loadFile,
          parseHTML,
      };
  })();

  /**
   * Global load function.
   * @param {string} path - The path to the HTML file (without .html extension).
   */
  window.load = function(path) {
      console.log(`Loading: ${path}`);

      if (Registry.isFileLoaded(path)) {
          console.log(`Already loading or loaded: ${path}`);
          return;
      }

      // Mark the file as loaded immediately to prevent race conditions
      Registry.markFileAsLoaded(path);

      (async function() {
          try {
              const responseText = await Loader.loadFile(path);
              const doc = Loader.parseHTML(responseText);

              // Extract the <template> with the 'name' attribute
              const template = doc.querySelector('template[name]');
              if (!template) {
                  console.error(`No <template name="..."> found in ${path}.html`);
                  return;
              }

              const componentName = template.getAttribute('name');
              const templateContent = template.innerHTML;

              // Extract the <script> content
              const script = doc.querySelector('script');
              const scriptContent = script ? script.textContent : '';

              // Use compiler to generate and define the component
              Compiler.compileComponent(componentName, templateContent, scriptContent);

              console.log(`Component prepared for registration: ${componentName}`);

              // If bundling mode is enabled, finalize the bundle after all components are loaded
              if (window.CatConfig.bundleAll) {
                  // Check if all components are loaded; in this simple implementation, we assume that
                  // finalizeBundle is called externally after all load calls
                  // Alternatively, implement a more sophisticated tracking mechanism
                  // For simplicity, we'll use a timeout to finalize after a brief delay
                  setTimeout(() => {
                      Compiler.finalizeBundle();
                  }, 0);
              }

          } catch (error) {
              console.error(`Error loading component from ${path}: ${error.message}`);
          }
      })();
  };

})();

---

You must set a logic that allow the engine, when the config it's set for compile all the code into a single blob, to compile all the components loaded first at any depth of nested components. Consider a component can load a component, that loads another four components, and so one. Our engine must put all that compiled code into a single blob.