// 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
  };

  /**
   * Registry module to track loaded components and dependencies.
   */
  const Registry = (function() {
      const loadedComponents = new Set();
      const pendingComponents = new Set();
      const componentDependencies = new Map(); // Map of component -> Set of dependencies

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

          /**
           * Marks a component as loaded.
           * @param {string} path - The path to the component.
           */
          markLoaded: (path) => loadedComponents.add(path),

          /**
           * Adds a component to the pending set.
           * @param {string} path - The path to the component.
           */
          addPending: (path) => pendingComponents.add(path),

          /**
           * Removes a component from the pending set.
           * @param {string} path - The path to the component.
           */
          removePending: (path) => pendingComponents.delete(path),

          /**
           * Checks if there are any pending components.
           * @returns {boolean} - True if pending, else false.
           */
          hasPending: () => pendingComponents.size > 0,

          /**
           * Adds a dependency for a component.
           * @param {string} component - The parent component.
           * @param {string} dependency - The dependent component.
           */
          addDependency: (component, dependency) => {
              if (!componentDependencies.has(component)) {
                  componentDependencies.set(component, new Set());
              }
              componentDependencies.get(component).add(dependency);
          },

          /**
           * Retrieves dependencies of a component.
           * @param {string} component - The component.
           * @returns {Set<string>} - Set of dependencies.
           */
          getDependencies: (component) => componentDependencies.get(component) || new Set(),

          /**
           * Retrieves all components.
           * @returns {Set<string>} - Set of all loaded components.
           */
          getAllComponents: () => loadedComponents,

          /**
           * Retrieves the dependency graph.
           * @returns {Map<string, Set<string>>} - Dependency graph.
           */
          getDependencyGraph: () => componentDependencies
      };
  })();

  /**
   * Loader module for fetching and parsing component 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;
    }

    /**
     * Extracts load() paths from script content using RegExp.
     * Assumes load() is called with string literals.
     * @param {string} scriptContent - The script content.
     * @returns {Array<string>} - Array of paths to load.
     */
    function extractLoadPaths(scriptContent) {
        const loadPaths = [];
        const loadRegex = /load\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
        let match;
        while ((match = loadRegex.exec(scriptContent)) !== null) {
            loadPaths.push(match[1]);
        }
        return loadPaths;
    }

    /**
     * Removes load() declarations from script content.
     * @param {string} scriptContent - The script content.
     * @returns {string} - The cleaned script content.
     */
    function removeLoadCalls(scriptContent) {
        return scriptContent.replace(/load\s*\(\s*['"]([^'"]+)['"]\s*\);?\s*/g, ''); // Added semicolon and optional whitespace
    }

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

  /**
   * Compiler module for generating and registering component classes using Blob URLs.
   */
  const Compiler = (function() {
      // Array to store compiled component codes in bundling mode
      let compiledComponents = [];

      /**
       * 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> (without load() calls).
       * @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.
       */
      function compileAndRegisterBundle() {
          if (compiledComponents.length === 0) return;

          // Combine all component codes into one string
          const bundledCode = compiledComponents.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.
       * @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);
      }

      return {
          /**
           * Compiles a component and adds it to the bundling array or registers immediately.
           * @param {string} name - The name of the component.
           * @param {string} templateContent - The template content.
           * @param {string} scriptContent - The script content (load() calls removed).
           */
          compileComponent: function(name, templateContent, scriptContent) {
              const classCode = compileComponentCode(name, templateContent, scriptContent);
              if (window.CatConfig.bundleAll) {
                  compiledComponents.push(classCode);
              } else {
                  compileAndRegisterComponent(name, classCode);
              }
          },

          /**
           * Finalizes the compilation process by injecting the bundle if bundling is enabled.
           */
          finalizeCompilation: function() {
              if (window.CatConfig.bundleAll) {
                  compileAndRegisterBundle();
              }
          }
      };
  })();

  /**
   * Main Engine module to orchestrate loading, parsing, and compiling components.
   */
  const Engine = (function() {
      /**
       * Recursively processes a component and its dependencies.
       * @param {string} path - The path to the component.
       * @returns {Promise<void>}
       */
      async function processComponent(path) {
          if (Registry.isLoaded(path)) {
              return;
          }

          Registry.markLoaded(path);
          Registry.addPending(path);

          try {
              const content = await Loader.loadFile(path);
              const doc = Loader.parseHTML(content);

              // 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 rawScriptContent = script ? script.textContent : '';
              const loadPaths = Loader.extractLoadPaths(rawScriptContent);

              // Remove load() calls from script
              const cleanedScriptContent = Loader.removeLoadCalls(rawScriptContent);

              // Record dependencies
              loadPaths.forEach(depPath => {
                  Registry.addDependency(path, depPath);
              });

              // Compile the component
              Compiler.compileComponent(componentName, templateContent, cleanedScriptContent);

              // Recursively process dependencies
              for (let depPath of loadPaths) {
                  await processComponent(depPath);
              }

              console.log(`Component "${componentName}" processed.`);

          } catch (error) {
              console.error(`Error processing component "${path}":`, error);
          } finally {
              Registry.removePending(path);
          }
      }

      /**
       * Initiates the loading and compilation process.
       * @param {Array<string>} initialPaths - Initial component paths to load.
       * @returns {Promise<void>}
       */
      async function initiateLoad(initialPaths) {
          const loadPromises = initialPaths.map(path => processComponent(path));
          await Promise.all(loadPromises);

          // Finalize compilation after all components are processed
          Compiler.finalizeCompilation();
      }

      function removeCatScript() {
        const catScript = document.querySelector('script[cat-engine]');
        if (catScript) {
            catScript.remove();
        }
      }


      return {
          /**
           * Starts the engine with specified initial components.
           * @param {Array<string>} initialPaths - Initial component paths to load.
           */
          start: function(initialPaths) {
              initiateLoad(initialPaths).then(() => {
                removeCatScript();
              });
          }
      };
  })();

  /**
   * Overriding the global load() function to integrate with the Engine.
   * Instead of loading immediately, it queues the path for processing.
   * Multiple load() calls are accumulated and processed together.
   */
  window.load = function(path) {
    if (window.CatConfig.bundleAll) {
        console.log(`Queued for bundling: ${path}`);
        if (!Engine._loadQueue) {
            Engine._loadQueue = new Set();
        }
        Engine._loadQueue.add(path);

        // Ensure Engine.start is called only once after all load() calls
        clearTimeout(Engine._startTimeout);
        Engine._startTimeout = setTimeout(() => {
            Engine.start(Array.from(Engine._loadQueue));
            Engine._loadQueue.clear(); // Clear the queue after processing
        }, 0);

    } else {
        console.log(`Queued for loading: ${path}`);
        Engine.start([path]);
    }
};

  /**
   * Extend the Engine to accumulate all loads for bundling.
   * This ensures that multiple load() calls are processed together.
   */
  (function extendLoadForBundling() {
      if (window.CatConfig.bundleAll) {
          const originalLoad = window.load;
          const loadQueue = new Set();

          // Override load to add paths to the queue instead of processing immediately
          window.load = function(path) {
              console.log(`Queued for bundling: ${path}`);
              loadQueue.add(path);
          };

          // Modify the Engine's start method to process all queued paths
          const originalStart = Engine.start;
          Engine.start = function(initialPaths) {
              initialPaths.forEach(path => loadQueue.add(path));
              if (Engine._processing) return; // Prevent multiple initiations
              Engine._processing = true;
              originalStart(Array.from(loadQueue)).then(() => {
                  Engine._processing = false;
              });
          };
      }
  })();

  /**
   * Optional: Automatically finalize bundling when the window loads.
   * This ensures that all load() calls made before window load are captured.
   */
  window.addEventListener('load', function() {
      if (window.CatConfig.bundleAll) {
          Engine.start([]); // Start processing all queued loads
      }
  });

})();


----

// compiler/compiler.js

const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');
const { Command } = require('commander');

const program = new Command();

// Define paths
const COMPONENTS_DIR = path.resolve(__dirname, '../app/comp');
const INDEX_HTML_SRC = path.resolve(__dirname, '../index.html');
const DIST_DIR = path.resolve(__dirname, '../dist');
const DIST_INDEX_HTML = path.resolve(DIST_DIR, 'index.html');
const BUNDLE_FILE = path.join(DIST_DIR, 'components.bundle.js');

// Register 'compile' command
program
  .command('compile')
  .description('Compile all components for production')
  .action(async () => {
    try {
      console.log('Starting compilation...');
      await prepareDist();
      const components = await loadComponents();
      const dependencyGraph = buildDependencyGraph(components);
      const sortedComponents = topologicalSort(dependencyGraph);
      const bundledCode = compileComponents(sortedComponents, components);
      await bundleComponents(bundledCode);
      await copyIndexHTML();
      console.log('Compilation and bundling completed successfully.');
    } catch (error) {
      console.error('Compilation failed:', error.message);
      process.exit(1);
    }
  });

program.parse(process.argv);

/**
 * Prepares the distribution directory.
 */
async function prepareDist() {
  await fs.ensureDir(DIST_DIR);
  // Clean previous build
  await fs.emptyDir(DIST_DIR);
  console.log('Prepared dist directory.');
}

/**
 * Loads all component files.
 * @returns {Object} - An object mapping component names to their content.
 */
async function loadComponents() {
  const componentFiles = glob.sync('**/*.html', { cwd: COMPONENTS_DIR });
  const components = {};

  for (const file of componentFiles) {
    const filePath = path.join(COMPONENTS_DIR, file);
    const content = await fs.readFile(filePath, 'utf-8');

    let componentName, templateContent, scriptContent;

    // Attempt to extract <template name="...">
    const templateNameMatch = content.match(/<template\s+name=['"]([\w-]+)['"]>/);
    if (templateNameMatch) {
      componentName = templateNameMatch[1];
      const templateMatch = content.match(/<template\s+name=['"][\w-]+['"]>([\s\S]*?)<\/template>/);
      templateContent = templateMatch ? templateMatch[1].trim() : '';
    } else {
      // Fallback: Use the root tag as the component name
      const rootTagMatch = content.match(/^<([\w-]+)>/);
      if (rootTagMatch) {
        componentName = rootTagMatch[1];
        const innerContentMatch = content.match(/^<[\w-]+>([\s\S]*?)<\/[\w-]+>$/);
        templateContent = innerContentMatch ? innerContentMatch[1].trim() : '';
      } else {
        throw new Error(`Component in ${file} is missing a <template name="..."> tag or a root custom element.`);
      }
    }

    if (components[componentName]) {
      throw new Error(`Duplicate component name detected: ${componentName}`);
    }

    // Extract script content
    const scriptMatch = templateContent.match(/<script>([\s\S]*?)<\/script>/);
    const rawScript = scriptMatch ? scriptMatch[1].trim() : '';

    // Extract dependencies from load() calls
    const dependencies = extractLoadCalls(rawScript);

    // Remove load() calls from script
    const cleanedScript = removeLoadCalls(rawScript);

    // Remove <script> tags from templateContent
    templateContent = templateContent.replace(/<script>[\s\S]*?<\/script>/, '').trim();

    components[componentName] = {
      name: componentName,
      template: templateContent,
      script: cleanedScript,
      dependencies
    };

    console.log(`Loaded component: ${componentName}`);
  }

  return components;
}

/**
 * Extracts paths from load() calls in script content.
 * @param {string} script - The script content.
 * @returns {Array<string>} - Array of dependency paths.
 */
function extractLoadCalls(script) {
  const loadRegex = /load\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
  const loadPaths = [];
  let match;

  while ((match = loadRegex.exec(script)) !== null) {
    loadPaths.push(match[1]);
  }

  return loadPaths;
}

/**
 * Removes load() calls from script content.
 * @param {string} script - The script content.
 * @returns {string} - Cleaned script without load() calls.
 */
function removeLoadCalls(script) {
  return script.replace(/load\s*\(\s*['"]([^'"]+)['"]\s*\);?/g, '').trim();
}

/**
 * Builds a dependency graph from components.
 * @param {Object} components - The components object.
 * @returns {Map<string, Set<string>>} - Dependency graph.
 */
function buildDependencyGraph(components) {
  const graph = new Map();

  for (const [name, component] of Object.entries(components)) {
    graph.set(name, new Set());

    for (const depPath of component.dependencies) {
      // Assume depPath is relative, e.g., 'app/comp/name'
      const depFileName = path.basename(depPath);
      const depName = depFileName; // Assuming component name matches file name without extension

      if (!components[depName]) {
        throw new Error(`Component "${name}" has missing dependency: "${depName}"`);
      }
      graph.get(name).add(depName);
    }
  }

  return graph;
}

/**
 * Performs topological sort on the dependency graph.
 * @param {Map<string, Set<string>>} graph - The dependency graph.
 * @returns {Array<string>} - Sorted components.
 */
function topologicalSort(graph) {
  const visited = new Set();
  const sorted = [];

  function visit(node, ancestors = []) {
    if (visited.has(node)) return;
    if (ancestors.includes(node)) {
      throw new Error(`Circular dependency detected: ${ancestors.join(' -> ')} -> ${node}`);
    }
    ancestors.push(node);
    for (const neighbor of graph.get(node)) {
      visit(neighbor, ancestors.slice());
    }
    visited.add(node);
    sorted.push(node);
  }

  for (const node of graph.keys()) {
    visit(node);
  }

  return sorted;
}

/**
 * Compiles components into JavaScript classes.
 * @param {Array<string>} sortedComponents - Components in load order.
 * @param {Object} components - All components.
 * @returns {string} - Combined JavaScript code.
 */
function compileComponents(sortedComponents, components) {
  let bundledCode = '';

  for (const componentName of sortedComponents) {
    const component = components[componentName];
    const className = toPascalCase(componentName);

    // Extract style if present
    const styleMatch = component.template.match(/<style>([\s\S]*?)<\/style>/);
    const styleContent = styleMatch ? styleMatch[1].trim() : '';

    // Extract inner HTML without style
    const htmlWithoutStyle = component.template.replace(/<style>[\s\S]*?<\/style>/, '').trim();

    // Escape backticks and dollar signs in HTML
    const escapedHTML = htmlWithoutStyle
      .replace(/`/g, '\\`')
      .replace(/\$/g, '\\$');

    // Process dynamic expressions in HTML
    const processedHTML = processDynamicExpressions(escapedHTML);

    // Process class properties and methods from script
    const { properties, methods, eventListeners } = parseScript(component.script);

    // Generate property initializations
    const propertyInitializations = Object.entries(properties)
      .map(([prop, value]) => `this.${prop} = ${value};`)
      .join('\n      ');

    // Generate method bindings
    const methodBindings = Object.keys(methods)
      .map(method => `this.${method} = this.${method}.bind(this);`)
      .join('\n      ');

    // Generate method definitions
    const methodDefinitions = Object.entries(methods)
      .map(([method, body]) => `${method}() {\n        ${body}\n      }`)
      .join('\n\n      ');

    // Generate event listeners
    const generatedEventListeners = eventListeners
      .map(listener => `this.shadowRoot.querySelector('${listener.selector}').addEventListener('${listener.event}', this.${listener.handler});`)
      .join('\n      ');

    // Construct the class definition
    const classDef = `
  class ${className} extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });

      ${styleContent ? `const style = document.createElement('style');
      style.textContent = \`
${indent(styleContent, 8)}
      \`;
      this.shadowRoot.appendChild(style);\n` : ''}

      this.shadowRoot.innerHTML += \`
${indent(processedHTML, 6)}
      \`;

      ${propertyInitializations}

      ${methodBindings}

      ${generatedEventListeners}
    }

    connectedCallback() {
      ${Object.keys(methods).length > 0 ? '// Bind methods\n' : ''}
      // Initialize component
      if (typeof this.init === 'function') {
        this.init();
      }
    }

    ${methodDefinitions}
  }

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

    bundledCode += classDef + '\n';
    console.log(`Compiled component: ${componentName}`);
  }

  return bundledCode;
}

/**
 * Converts kebab-case to PascalCase.
 * @param {string} str - The kebab-case string.
 * @returns {string} - PascalCase string.
 */
function toPascalCase(str) {
  return str
    .split('-')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join('');
}

/**
 * Indents each line of the input string by the specified number of spaces.
 * @param {string} str - The input string.
 * @param {number} spaces - Number of spaces to indent.
 * @returns {string} - Indented string.
 */
function indent(str, spaces) {
  const padding = ' '.repeat(spaces);
  return str
    .split('\n')
    .map(line => padding + line)
    .join('\n');
}

/**
 * Processes dynamic expressions in HTML (e.g., {property}).
 * Replaces them with template literals to allow dynamic updates.
 * @param {string} html - The HTML string with dynamic expressions.
 * @returns {string} - Processed HTML string.
 */
function processDynamicExpressions(html) {
  // Replace {property} with \${this.property}
  return html.replace(/\{([\w.]+)\}/g, '${this.$1}');
}

/**
 * Parses the script content to extract properties, methods, and event listeners.
 * @param {string} script - The cleaned script content.
 * @returns {Object} - Contains properties, methods, and eventListeners.
 */
function parseScript(script) {
  const lines = script.split('\n').map(line => line.trim()).filter(line => line.length > 0);
  const properties = {};
  const methods = {};
  const eventListeners = [];

  const propertyRegex = /^this\.(\w+)\s*=\s*(.+);$/;
  const functionRegex = /^function\s+(\w+)\s*\(([^)]*)\)\s*\{([\s\S]*)\}$/;
  const eventListenerRegex = /^([a-zA-Z0-9_$.]+)\.addEventListener\(['"](\w+)['"],\s*(\w+)\);?$/;

  for (let line of lines) {
    // Property initialization
    let propMatch = line.match(propertyRegex);
    if (propMatch) {
      const propName = propMatch[1];
      const propValue = propMatch[2];
      properties[propName] = propValue;
      continue;
    }

    // Function definition
    let funcMatch = line.match(functionRegex);
    if (funcMatch) {
      const funcName = funcMatch[1];
      const params = funcMatch[2];
      const body = funcMatch[3];
      methods[funcName] = body;
      continue;
    }

    // Event listener
    let eventMatch = line.match(eventListenerRegex);
    if (eventMatch) {
      const selector = extractSelector(eventMatch[1]);
      const eventType = eventMatch[2];
      const handler = eventMatch[3];
      eventListeners.push({ selector, event: eventType, handler });
      continue;
    }

    // Other lines can be added as needed
  }

  return { properties, methods, eventListeners };
}

/**
 * Extracts the selector from a querySelector string.
 * @param {string} query - The querySelector string (e.g., "this.shadowRoot.querySelector('button')").
 * @returns {string} - The selector string (e.g., "button").
 */
function extractSelector(query) {
  const selectorMatch = query.match(/querySelector\(['"]([^'"]+)['"]\)/);
  return selectorMatch ? selectorMatch[1] : '';
}

/**
 * Bundles the compiled code into a single JavaScript file.
 * @param {string} code - The combined JavaScript code.
 */
async function bundleComponents(code) {
  // For simplicity, wrap the code in an IIFE to avoid global scope pollution
  const wrappedCode = `(function() {\n${code}\n})();\n`;

  await fs.writeFile(BUNDLE_FILE, wrappedCode, 'utf-8');
  console.log(`Bundled components into ${path.relative(DIST_DIR, BUNDLE_FILE)}`);
}

/**
 * Copies and modifies index.html to include the bundled script.
 */
async function copyIndexHTML() {
  const indexContent = await fs.readFile(INDEX_HTML_SRC, 'utf-8');

  // Inject the bundled script before closing </body> tag
  const scriptTag = `<script src="components.bundle.js"></script>`;
  const modifiedIndex = indexContent.replace(
    /<\/body>/i,
    `  ${scriptTag}\n</body>`
  );

  await fs.writeFile(DIST_INDEX_HTML, modifiedIndex, 'utf-8');
  console.log(`Generated dist/index.html with bundled components.`);
}


----

As you can notice the compiler.j it's not compiling the component in the same way cat.js it's doing. Your task it's guarantee that the compiler.js output the same code for compiled component as cat.js does. Must be exact. When finish output the compiler.js