// Updated cat.js with initialization order fix

(function() {
  'use strict';

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

    return {
      isFileLoaded: (path) => loadedFiles.has(path),
      markFileAsLoaded: (path) => loadedFiles.add(path),
    };
  })();

  /**
   * Compiler module for generating optimized component classes.
   */
  const Compiler = (function() {
    /**
     * Compiles the component's template and script into an optimized component 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>.
     */
    function compileComponent(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;

      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

      // Extract function definitions and remaining code from scriptContent
      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;

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

  ${getterSetterCode}

  ${classMethodsCode}

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

      // Output the compiled class code
      console.log(`Compiled component class for ${name}:\n${classCode}`);

      // Evaluate the class code
      try {
        eval(classCode);
      } catch (error) {
        console.error(`Error compiling component ${name}: ${error.message}`);
        console.error(error);
        console.log(classCode);
      }
    }

    return {
      compileComponent,
    };
  })();

  /**
   * 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 registered: ${componentName}`);
      } catch (error) {
        console.error(`Error loading component from ${path}: ${error.message}`);
      }
    })();
  };
})();

---

Add the compiler the connect and disconnect Callback to mount and unmount the components automatically, so the dev doesn't have to worry about.