1. Project Overview
Cat.js is a lightweight, state-of-the-art engine designed to build powerful User Interfaces (UIs). It prioritizes the highest performance and efficiency, based exclusively on modern native browser APIs, primarily Web Components.

- Version: 0.1.0 (as of October 1, 2024)

2. Core Concepts and Principles
- Utilizes modern native browser APIs, especially Web Components
- Focuses on maximum performance and efficiency
- Promotes a lightweight approach to UI development
- Emphasizes simplicity and ease of use

3. Technical Details

Key Features
- Client-Side Compiler (CSC) before loading the DOM
- Attachment through a script:src tag in the head of HTML documents
- Written in vanilla, low-level JavaScript optimized for browser runtime
- Beautiful and easy-to-use syntax
- Efficient project tree structure

Implementation Details
- No Virtual DOM or similar abstractions
- Avoids use of bundlers, Webpack, polyfills, etc.
- Discourages writing HTML or CSS code within JavaScript files
- Eliminates render methods for components and logic that trigger unnecessary reflows and repainting

4. Code

```

// cat.js

// Immediately Invoked Function Expression (IIFE) to encapsulate the compiler
(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 self-contained component classes.
   */
  const Compiler = (function() {
    /**
     * Compiles the component's template and script into a 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) {
      // Process the templateContent here, parse it into DOM nodes, and find bindings.

      // Create a template element
      const templateElement = document.createElement('template');
      templateElement.innerHTML = templateContent;

      // Now, process the template content to find bindings
      const bindings = []; // Array to hold binding info
      const propertyNames = new Set();

      const processNode = (node) => {
        if (node.nodeType === Node.TEXT_NODE) {
          const text = node.textContent;
          const parts = text.split(/(\{|\})/);
          let hasBinding = false;

          // Collect variable names inside { }
          for (let i = 0; i < parts.length; i++) {
            if (parts[i] === '{' && parts[i + 2] === '}') {
              const varName = parts[i + 1].trim();
              propertyNames.add(varName);
              hasBinding = true;

              i += 2; // Skip over the { varName }
            }
          }

          if (hasBinding) {
            bindings.push({
              type: 'text',
              nodePath: getNodePath(node),
              varNames: getVariableNamesFromParts(parts),
              parts: parts,
            });
          }
        } else if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.nodeName.toLowerCase() === 'style') {
            // Do not process the content of style tags
            return;
          }

          // Process attributes
          for (let attr of Array.from(node.attributes)) {
            const value = attr.value;
            const parts = value.split(/(\{|\})/);
            let hasBinding = false;

            // Collect variable names inside { }
            for (let i = 0; i < parts.length; i++) {
              if (parts[i] === '{' && parts[i + 2] === '}') {
                const varName = parts[i + 1].trim();
                propertyNames.add(varName);
                hasBinding = true;

                i += 2; // Skip over the { varName }
              }
            }

            if (hasBinding) {
              bindings.push({
                type: 'attribute',
                nodePath: getNodePath(node),
                attributeName: attr.name,
                varNames: getVariableNamesFromParts(parts),
                parts: parts,
              });
            }
          }

          // Process child nodes
          node.childNodes.forEach(childNode => {
            processNode(childNode);
          });
        }
      };

      // Helper function to get the path to a node
      const getNodePath = (node) => {
        const path = [];
        let current = node;
        while (current !== templateElement.content) {
          const parent = current.parentNode;
          const index = Array.prototype.indexOf.call(parent.childNodes, current);
          path.unshift(index);
          current = parent;
        }
        return path;
      };

      // Helper function to extract variable names from parts
      const getVariableNamesFromParts = (parts) => {
        const varNames = [];
        for (let i = 0; i < parts.length; i++) {
          if (parts[i] === '{' && parts[i + 2] === '}') {
            varNames.push(parts[i + 1].trim());
            i += 2; // Skip over the { varName }
          }
        }
        return varNames;
      };

      // Start processing from templateElement.content
      templateElement.content.childNodes.forEach(node => processNode(node));

      // Precompute propertyNames
      const properties = Array.from(propertyNames);

      // Generate the ComponentClass
      class ComponentClass extends HTMLElement {
        constructor() {
          super();
          const shadowRoot = this.attachShadow({ mode: 'open' });

          // Clone the template content into the shadowRoot
          shadowRoot.appendChild(templateElement.content.cloneNode(true));

          // Map from property name to array of bindings
          this._bindingsMap = {};

          // Setup bindings
          this._bindings = this._createBindings(shadowRoot);

          // Initialize properties and their setters/getters
          properties.forEach(varName => {
            this._defineReactiveProperty(varName);
            this[varName] = ''; // Initialize property
          });

          // Execute user script in the context of 'this' safely
          if (scriptContent.trim()) {
            try {
              const scriptFunction = new Function(scriptContent);
              scriptFunction.call(this);
            } catch (error) {
              console.error(`Error executing script in component ${name}: ${error.message}`);
            }
          }
        }

        _createBindings(shadowRoot) {
          const instanceBindings = [];

          bindings.forEach(binding => {
            const node = this._getNodeByPath(shadowRoot, binding.nodePath);

            // Create a copy of the binding for this instance
            const instanceBinding = Object.assign({}, binding);
            instanceBinding.node = node;

            // For each varName in the binding, add the binding to _bindingsMap
            instanceBinding.varNames.forEach(varName => {
              if (!this._bindingsMap[varName]) {
                this._bindingsMap[varName] = [];
              }
              this._bindingsMap[varName].push(instanceBinding);
            });

            instanceBindings.push(instanceBinding);
          });

          return instanceBindings;
        }

        _defineReactiveProperty(varName) {
          let value = '';
          Object.defineProperty(this, varName, {
            get: () => value,
            set: newValue => {
              value = newValue;
              this._updateProperty(varName);
            },
          });
        }

        _updateProperty(varName) {
          const bindings = this._bindingsMap[varName];
          if (bindings) {
            bindings.forEach(binding => {
              if (binding.type === 'text') {
                const newText = this._computeBindingText(binding);
                binding.node.textContent = newText;
              } else if (binding.type === 'attribute') {
                const newValue = this._computeBindingText(binding);
                binding.node.setAttribute(binding.attributeName, newValue);
              }
            });
          }
        }

        _computeBindingText(binding) {
          const computedParts = [];
          for (let i = 0; i < binding.parts.length; i++) {
            if (binding.parts[i] === '{' && binding.parts[i + 2] === '}') {
              const varName = binding.parts[i + 1].trim();
              computedParts.push(this[varName]);
              i += 2; // Skip over { varName }
            } else if (binding.parts[i] !== '{' && binding.parts[i] !== '}') {
              computedParts.push(binding.parts[i]);
            }
          }
          return computedParts.join('');
        }

        connectedCallback() {
          // Component is connected to the DOM
          // Any additional initialization can be done here
        }

        disconnectedCallback() {
          // Clean up if necessary
        }

        _getNodeByPath(root, path) {
          let current = root;
          path.forEach(index => {
            current = current.childNodes[index];
          });
          return current;
        }
      }

      // Register the component
      customElements.define(name, ComponentClass);
    }

    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).
   * @returns {Promise<void>}
   */
  window.load = async function(path) {
    console.log(`Loading: ${path}`);

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

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

      Registry.markFileAsLoaded(path);

      // 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}`);
    }
  };
})();

```

5. Sample component:

```
<!-- app/comp/counter.html -->
<template name="my-counter">
    <style>
        .on {
            background-color: blue;
        }
        .off {
            background-color: aliceblue;
        }
        .btn {
            padding: 20px;
        }
    </style>

    <button class="{status} btn">Count: {count}</button>
</template>

<script>
    this.count = 0;
    this.status = 'on';
    
    const button = this.shadowRoot.querySelector('button');
    const span = button.querySelector('span');

    const component = this; // To maintain correct context inside functions

    function updateCount() {
        component.count++;

        component.status = component.status === 'on' ? 'off' : 'on';
        // The reactive system will automatically update the DOM elements where {count} and {status} are used
    }

    button.addEventListener('click', updateCount);
</script>
```

Explain step by step how our compiler works, and how manage reactive values. Show how our sample component looks after being compiled.