project-root/
├── cat/
│   ├── bin/
│   │   └── cat.js
│   └── src/
│       └── compiler.js
├── index.html
├── package.json
└── cat.config.json

Syntax

```
{filename}.comp.html
<{comp name}>

</{comp name}>

<script>

</script>

<style>

</style>

{filename}.page.html
<page route=&quot;path&quot;>

</page>

Any .html file
<load route=&quot;path&quot;
```

bin.js
```
#!/usr/bin/env node

const { program } = require('commander');
const path = require('path');
const fs = require('fs-extra');
const compiler = require('../src/compiler');
const express = require('express');

/**
 * Initialize the package.json and cat.config.json
 */
function initializeProject() {
  const projectRoot = process.cwd();
  const packageJsonPath = path.join(projectRoot, 'package.json');
  const configPath = path.join(projectRoot, 'cat.config.json');

  // Initialize package.json if it doesn't exist
  if (!fs.existsSync(packageJsonPath)) {
    const defaultPackageJson = {
      name: path.basename(projectRoot),
      version: &quot;1.0.0&quot;,
      description: &quot;A project using the Cat framework.&quot;,
      main: &quot;index.html&quot;,
      scripts: {
        cat: &quot;node ./cat/bin/cat.js&quot;
      },
      dependencies: {
        &quot;commander&quot;: &quot;^10.0.0&quot;,
        &quot;express&quot;: &quot;^4.18.2&quot;,
        &quot;fs-extra&quot;: &quot;^11.1.1&quot;,
        &quot;glob&quot;: &quot;^10.3.3&quot;
      },
      author: &quot;Your Name&quot;,
      license: &quot;MIT&quot;
    };
    fs.writeJsonSync(packageJsonPath, defaultPackageJson, { spaces: 2 });
    console.log('✅ package.json has been created at the project root.');
    console.log('👉 Please run &quot;npm install&quot; to install the dependencies.');
  } else {
    console.log('ℹ️  package.json already exists.');
  }

  // Initialize cat.config.json if it doesn't exist
  if (!fs.existsSync(configPath)) {
    const defaultConfig = {
      root: 'index.html'
    };
    fs.writeJsonSync(configPath, defaultConfig, { spaces: 2 });
    console.log('✅ cat.config.json has been created at the project root.');
  } else {
    console.log('ℹ️  cat.config.json already exists.');
  }
}

/**
 * Start the development server
 */
async function startServer() {
  const projectRoot = process.cwd();
  const configPath = path.join(projectRoot, 'cat.config.json');

  if (!fs.existsSync(configPath)) {
    console.error('❌ Error: cat.config.json not found. Please run &quot;npm run cat init&quot; first.');
    process.exit(1);
  }

  const config = fs.readJsonSync(configPath);
  const indexPath = path.join(projectRoot, config.root);

  if (!fs.existsSync(indexPath)) {
    console.error(`❌ Error: Root file &quot;${config.root}&quot; not found.`);
    process.exit(1);
  }

  const app = express();
  const PORT = 3000;

  app.get('*', async (req, res) => {
    try {
      const compiledHTML = await compiler.compile(indexPath);
      res.send(compiledHTML);
    } catch (error) {
      console.error('❌ Compilation Error:', error);
      res.status(500).send('Internal Server Error');
    }
  });

  app.listen(PORT, () => {
    console.log(`🚀 Development server is running at http://localhost:${PORT}`);
  });
}

// Define the version
program.version('1.0.0');

// Define the init command
program
  .command('init')
  .description('Initialize package.json and cat.config.json at the project root')
  .action(initializeProject);

// Define the start command
program
  .command('start')
  .description('Start the development server')
  .action(startServer);

// Parse the command-line arguments
program.parse(process.argv);
```

compiler
```
// cat/src/compiler.js

const fs = require('fs-extra');
const path = require('path');
const glob = require('glob');

/**
 * Parse a component file to extract tagName, HTML, script, and style sections.
 * @param {string} filePath - Path to the .comp.html file.
 * @returns {Promise<{ tagName: string, html: string, script: string, style: string }>} - Component details.
 */
async function parseComponent(filePath) {
  const content = await fs.readFile(filePath, 'utf-8');

  // Extract the outermost tag name
  const tagRegex = /<\s*([\w-]+)(\s+[^>]*)?>/i;
  const tagMatch = content.match(tagRegex);

  if (!tagMatch) {
    throw new Error(`No valid root tag found in ${path.basename(filePath)}`);
  }

  let tagName = tagMatch[1];

  // Ensure the tag name contains a hyphen
  if (!tagName.includes('-')) {
    tagName = `${tagName}-cat`;
  }

  // Extract HTML content inside the root tag
  const htmlRegex = new RegExp(`<${tagMatch[1]}[^>]*>([\\s\\S]*?)<\\/${tagMatch[1]}>`, 'i');
  const htmlMatch = content.match(htmlRegex);
  const htmlContent = htmlMatch ? htmlMatch[1].trim() : '';

  // Extract <script> section
  const scriptRegex = /<script[^>]*>([\s\S]*?)<\/script>/i;
  const scriptMatch = content.match(scriptRegex);
  const scriptContent = scriptMatch ? scriptMatch[1].trim() : '';

  // Extract <style> section
  const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/i;
  const styleMatch = content.match(styleRegex);
  const styleContent = styleMatch ? styleMatch[1].trim() : '';

  return {
    tagName,
    html: htmlContent,
    script: scriptContent,
    style: styleContent
  };
}

/**
 * Identify reactive expressions within a string.
 * @param {string} str - The string to search for expressions.
 * @returns {Array<string>} - Array of expression names.
 */
function identifyExpressions(str) {
  const exprRegex = /\{([\w$]+)\}/g;
  const expressions = [];
  let match;
  while ((match = exprRegex.exec(str)) !== null) {
    expressions.push(match[1]);
  }
  return expressions;
}

/**
 * Generate the class name in PascalCase from a tag name.
 * @param {string} tagName - The custom element tag name.
 * @returns {string} - PascalCase class name.
 */
function toPascalCase(tagName) {
  return tagName
    .split('-')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join('');
}

/**
 * Escape backticks in templates to avoid breaking template literals.
 * @param {string} str - The template string.
 * @returns {string} - Escaped string.
 */
function escapeBackticks(str) {
  return str.replace(/`/g, '\\`');
}

/**
 * Generate the registration script for the used components.
 * @param {Map<string, string>} components - Map of tagName to compiled class code.
 * @returns {string} - JavaScript code for registering components.
 */
function generateRegistrationScript(components) {
  let script = '';

  components.forEach((classCode, tagName) => {
    script += `
${classCode}
customElements.define('${tagName}', ${toPascalCase(tagName)});
`;
  });

  return script;
}

/**
 * Compiler Function
 * @param {string} indexPath - Path to the index.html file.
 * @returns {Promise<string>} - Compiled HTML content with component registrations and page injections.
 */
async function compile(indexPath) {
  // Read the index.html content
  let indexContent = await fs.readFile(indexPath, 'utf-8');

  // Define project root
  const projectRoot = path.dirname(indexPath);

  // -----------------------
  // Step 1: Process Components
  // -----------------------

  // Find all .comp.html files in the project directory
  const compFiles = glob.sync('**/*.comp.html', {
    cwd: projectRoot,
    absolute: true,
    ignore: ['node_modules/**', 'cat/**'] // Adjust ignore patterns as needed
  });

  const componentMap = new Map(); // Map of tagName to compiled class code
  const parsedComponents = new Set(); // To track parsed components

  // Parse and compile each component file
  for (const file of compFiles) {
    try {
      const { tagName, html, script, style } = await parseComponent(file);

      if (parsedComponents.has(tagName)) {
        console.log(`ℹ️  Component <${tagName}> from ${path.relative(projectRoot, file)} has already been parsed.`);
        continue;
      }

      // Identify expressions in HTML
      const expressions = identifyExpressions(html);

      // Parse script to extract properties and methods
      const scriptLines = script.split('\n').map(line => line.trim()).filter(line => line);
      const properties = [];
      const methods = [];

      scriptLines.forEach(line => {
        // Match variable declarations: let name = 'value';
        const varMatch = line.match(/^let\s+([\w$]+)\s*=\s*['&quot;`](.*)['&quot;`];?$/);
        if (varMatch) {
          properties.push({ name: varMatch[1], value: varMatch[2] });
          return;
        }

        // Match function declarations: function funcName() { ... }
        const funcMatch = line.match(/^function\s+([\w$]+)\s*\(([^)]*)\)\s*\{([\s\S]*)\}$/);
        if (funcMatch) {
          const [, funcName, params, body] = funcMatch;
          methods.push({ name: funcName, params, body });
          return;
        }
      });

      // Generate class code
      const className = toPascalCase(tagName);
      let classCode = `class ${className} extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
`;

      // Initialize properties
      properties.forEach(prop => {
        classCode += `    this._${prop.name} = \`${escapeBackticks(prop.value)}\`;\n`;
      });

      // Build DOM
      classCode += `
    this._buildDOM();
  }

  _buildDOM() {
    const fragment = document.createDocumentFragment();

`;

      // Add styles
      if (style) {
        classCode += `    const style = document.createElement('style');
    style.textContent = \`
${escapeBackticks(style)}
    \`;
    fragment.appendChild(style);

`;
      }

      // Parse HTML and generate DOM creation code
      const domCode = generateDOMCode(html);
      classCode += domCode;

      classCode += `
    this.shadowRoot.appendChild(fragment);
  }

  connectedCallback() {
`;

      // Attach event listeners
      methods.forEach(method => {
        // Find event listeners in the DOM (e.g., onclick=&quot;methodName&quot;)
        // This implementation assumes that event listeners are specified with standard DOM event attributes
        // e.g., onclick=&quot;handleClick&quot;
        // Extract all elements with event attributes and attach listeners
      });

      // Attach event listeners based on methods
      // For simplicity, we'll parse the methods and assume that event listeners are defined in the HTML with attributes like onclick=&quot;methodName&quot;
      // This requires that during DOM generation, we store references to elements that have event listeners

      // Assuming that _eventListeners is an array of { element: reference, event: 'click', handler: 'methodName' }
      // We'll have to implement this during DOM generation

      // For this implementation, let's assume that event listeners are already attached during _buildDOM
      // Alternatively, you can enhance the DOM generation to collect event listeners and attach them here

      classCode += `  }

  disconnectedCallback() {
    // Clean up event listeners if necessary
  }

`;

      // Generate getters and setters for properties
      properties.forEach(prop => {
        // Getter
        classCode += `  get ${prop.name}() {
    return this._${prop.name};
  }

  set ${prop.name}(value) {
    if (this._${prop.name} !== value) {
      this._${prop.name} = value;
      this._update${toPascalCase(prop.name)}();
    }
  }

`;

        // Update method
        classCode += `  _update${toPascalCase(prop.name)}() {
    if (this._${prop.name}Node) {
      this._${prop.name}Node.textContent = \`${escapeBackticks('{')}${prop.name}${escapeBackticks('}')}\`.replace('{${prop.name}}', this._${prop.name});
    }
    if (this._${prop.name}Attr) {
      this._${prop.name}Attr.value = this._${prop.name};
    }
  }

`;
      });

      // Add methods
      methods.forEach(method => {
        classCode += `  ${method.name}(${method.params}) {
${method.body.split('\n').map(line => '    ' + line).join('\n')}
  }

`;
      });

      classCode += `}\n`;

      // Store the compiled class code
      componentMap.set(tagName, classCode);
      parsedComponents.add(tagName);

      console.log(`🔍 Parsed and compiled component: <${tagName}> from ${path.relative(projectRoot, file)}`);
    } catch (error) {
      console.error(`❌ Failed to parse ${file}: ${error.message}`);
    }
  }

  if (componentMap.size === 0) {
    console.warn('⚠️  No components found to compile.');
  }

  // -----------------------
  // Step 2: Detect Used Components
  // -----------------------

  // Find all .html files in the project directory for usage detection
  const htmlFiles = glob.sync('**/*.html', {
    cwd: projectRoot,
    absolute: true,
    ignore: ['node_modules/**', 'cat/**']
  });

  const htmlContents = [];
  for (const file of htmlFiles) {
    try {
      const content = await fs.readFile(file, 'utf-8');
      htmlContents.push(content);
    } catch (error) {
      console.error(`❌ Failed to read HTML file ${file}: ${error.message}`);
    }
  }

  // Detect which components are used across all HTML files
  const availableComponents = new Set(componentMap.keys());
  const usedComponents = detectUsedComponents(htmlContents, availableComponents);

  if (usedComponents.size === 0) {
    console.warn('⚠️  No used components found in HTML files.');
  } else {
    console.log(`📦 Used components: ${[...usedComponents].join(', ')}`);
  }

  // -----------------------
  // Step 3: Generate Registration Script
  // -----------------------

  const registrationScript = generateRegistrationScript(
    Array.from(usedComponents).reduce((map, tag) => map.set(tag, componentMap.get(tag)), new Map())
  );

  // -----------------------
  // Step 4: Inject Registration Script
  // -----------------------

  if (registrationScript.trim()) {
    // Inject the registration script before the closing </body> tag
    const scriptTag = `<script>
${registrationScript}
</script>
`;

    if (indexContent.includes('</body>')) {
      indexContent = indexContent.replace('</body>', `${scriptTag}</body>`);
    } else {
      // If no </body> tag, append the script at the end
      indexContent += `\n${scriptTag}`;
    }
    console.log('📝 Injected component registration scripts.');
  }

  // -----------------------
  // Step 5: Handle <load> Tags and Inject Page Content
  // -----------------------

  // Find and replace <load route=&quot;...&quot;> tags with corresponding page content
  const loadTagRegex = /<load\s+[^>]*route=[&quot;']([^&quot;']+)[&quot;'][^>]*>/gi;

  // Process <load> tags in the index.html
  indexContent = indexContent.replace(loadTagRegex, (match, route) => {
    // Path to the page content
    const pageFilePath = path.join(projectRoot, `${route}.page.html`);
    if (fs.existsSync(pageFilePath)) {
      const pageContent = fs.readFileSync(pageFilePath, 'utf-8');
      console.log(`🔄 Injected content for route &quot;${route}&quot;`);
      return pageContent;
    } else {
      console.error(`❌ No page found for route &quot;${route}&quot;.`);
      return match; // Keep the <load> tag if no content is found
    }
  });

  // -----------------------
  // Step 6: Return Compiled Content
  // -----------------------

  return indexContent;
}

/**
 * Detect used components in the provided HTML contents by parsing the HTML tags.
 * @param {Array<string>} htmlContents - Array of HTML file contents.
 * @param {Set<string>} availableComponents - Set of all available component tag names.
 * @returns {Set<string>} - Set of used component tag names.
 */
function detectUsedComponents(htmlContents, availableComponents) {
  const usedComponents = new Set();

  // Regex to match all tags in the HTML
  const tagRegex = /<\s*([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g;

  htmlContents.forEach(content => {
    let match;
    while ((match = tagRegex.exec(content)) !== null) {
      let tag = match[1];
      // If tag doesn't contain '-', append '-cat'
      if (!tag.includes('-')) {
        tag = `${tag}-cat`;
      }
      if (availableComponents.has(tag)) {
        usedComponents.add(tag);
      }
    }
  });

  return usedComponents;
}

/**
 * Generate DOM creation code from HTML.
 * This is a simplistic parser and may need enhancements for complex HTML structures.
 * @param {string} html - The inner HTML string of the component.
 * @returns {string} - JavaScript code for building the DOM.
 */
function generateDOMCode(html) {
  // This function parses the HTML and generates JavaScript code using document.createElement
  // For demonstration purposes, this is a rudimentary parser and may not handle all HTML features.
  // For a more robust solution, consider using an HTML parser library.

  // Split the HTML into lines and parse manually
  const lines = html.split('\n').map(line => line.trim()).filter(line => line);
  let code = '';

  // Stack to manage element hierarchy
  const stack = [];

  lines.forEach(line => {
    const openingTagMatch = line.match(/^<([a-zA-Z][\w-]*)([^>]*)>/);
    const closingTagMatch = line.match(/^<\/([a-zA-Z][\w-]*)>/);
    const selfClosingTagMatch = line.match(/^<([a-zA-Z][\w-]*)([^>]*)\/>/);

    if (selfClosingTagMatch) {
      const [ , tagName, attrs ] = selfClosingTagMatch;
      const varName = generateVarName(tagName);
      code += `    const ${varName} = document.createElement('${tagName}');\n`;

      // Handle attributes
      const attrRegex = /([a-zA-Z-:]+)=&quot;([^&quot;]+)&quot;/g;
      let attrMatch;
      while ((attrMatch = attrRegex.exec(attrs)) !== null) {
        const [ , attrName, attrValue ] = attrMatch;
        if (attrName.startsWith('on')) {
          // Event listener
          const event = attrName.substring(2);
          const handlerMatch = attrValue.match(/^([a-zA-Z][\w$]*)\((.*)\)$/);
          if (handlerMatch) {
            const handlerName = handlerMatch[1];
            const handlerArgs = handlerMatch[2];
            code += `    ${varName}.addEventListener('${event}', this.${handlerName}.bind(this));\n`;
          } else {
            code += `    ${varName}.addEventListener('${event}', this.${attrValue}.bind(this));\n`;
          }
        } else {
          // Check for reactive expressions in attributes
          const exprMatch = attrValue.match(/^\{([\w$]+)\}$/);
          if (exprMatch) {
            const exprName = exprMatch[1];
            code += `    this._${exprName}Attr = ${varName}.setAttribute('${attrName}', \`\${this.${exprName}}\`);\n`;
          } else {
            code += `    ${varName}.setAttribute('${attrName}', \`${escapeBackticks(attrValue)}\`);\n`;
          }
        }
      }

      stack[stack.length - 1].appendChild(varName);
      stack.push({ tagName, varName });
      stack.pop(); // Self-closing tag, no need to keep in stack
      code += `    ${stack[stack.length - 1].varName}.appendChild(${varName});\n`;
    } else if (openingTagMatch) {
      const [ , tagName, attrs ] = openingTagMatch;
      const varName = generateVarName(tagName);
      code += `    const ${varName} = document.createElement('${tagName}');\n`;

      // Handle attributes
      const attrRegex = /([a-zA-Z-:]+)=&quot;([^&quot;]+)&quot;/g;
      let attrMatch;
      while ((attrMatch = attrRegex.exec(attrs)) !== null) {
        const [ , attrName, attrValue ] = attrMatch;
        if (attrName.startsWith('on')) {
          // Event listener
          const event = attrName.substring(2);
          const handlerMatch = attrValue.match(/^([a-zA-Z][\w$]*)\((.*)\)$/);
          if (handlerMatch) {
            const handlerName = handlerMatch[1];
            const handlerArgs = handlerMatch[2];
            code += `    ${varName}.addEventListener('${event}', this.${handlerName}.bind(this));\n`;
          } else {
            code += `    ${varName}.addEventListener('${event}', this.${attrValue}.bind(this));\n`;
          }
        } else {
          // Check for reactive expressions in attributes
          const exprMatch = attrValue.match(/^\{([\w$]+)\}$/);
          if (exprMatch) {
            const exprName = exprMatch[1];
            code += `    this._${exprName}Attr = ${varName}.setAttribute('${attrName}', \`\${this.${exprName}}\`);\n`;
          } else {
            code += `    ${varName}.setAttribute('${attrName}', \`${escapeBackticks(attrValue)}\`);\n`;
          }
        }
      }

      // Handle text content or child elements
      // This simplistic parser assumes that text nodes come immediately after the opening tag
      // For complex HTML structures, consider using a proper HTML parser
      const textMatch = line.match(/^<[\w-]+[^>]*>\s*(.*?)\s*$/);
      if (textMatch &amp;&amp; textMatch[1]) {
        const textContent = textMatch[1];
        // Check for reactive expressions in text
        const exprMatch = textContent.match(/^\{([\w$]+)\}$/);
        if (exprMatch) {
          const exprName = exprMatch[1];
          code += `    this._${exprName}Node = document.createTextNode(\`\${this.${exprName}}\`);\n`;
          code += `    ${varName}.appendChild(this._${exprName}Node);\n`;
        } else {
          code += `    ${varName}.textContent = \`${escapeBackticks(textContent)}\`;\n`;
        }
      }

      // Append to parent
      if (stack.length === 0) {
        code += `    fragment.appendChild(${varName});\n`;
      } else {
        code += `    ${stack[stack.length - 1].varName}.appendChild(${varName});\n`;
      }

      // Push to stack
      stack.push({ tagName, varName });
    } else if (closingTagMatch) {
      // Pop the last element from the stack
      stack.pop();
    } else {
      // Handle text nodes outside of tags
      const textContent = line;
      // Check for reactive expressions in text
      const exprMatch = textContent.match(/^\{([\w$]+)\}$/);
      if (exprMatch) {
        const exprName = exprMatch[1];
        const varName = generateVarName('text');
        code += `    const ${varName} = document.createTextNode(\`\${this.${exprName}}\`);\n`;
        code += `    this._${exprName}Node = ${varName};\n`;
        if (stack.length === 0) {
          code += `    fragment.appendChild(${varName});\n`;
        } else {
          code += `    ${stack[stack.length - 1].varName}.appendChild(${varName});\n`;
        }
      } else {
        code += `    const textNode = document.createTextNode(\`${escapeBackticks(textContent)}\`);\n`;
        if (stack.length === 0) {
          code += `    fragment.appendChild(textNode);\n`;
        } else {
          code += `    ${stack[stack.length - 1].varName}.appendChild(textNode);\n`;
        }
      }
    }
  });

  return code;
}

/**
 * Generate a unique variable name based on the tag name.
 * @param {string} tagName - The tag name of the element.
 * @returns {string} - A unique variable name.
 */
function generateVarName(tagName) {
  // Simple counter-based variable name generation
  if (!generateVarName.counter) {
    generateVarName.counter = {};
  }
  if (!generateVarName.counter[tagName]) {
    generateVarName.counter[tagName] = 1;
  } else {
    generateVarName.counter[tagName]++;
  }
  return `${tagName.replace(/-/g, '_')}_${generateVarName.counter[tagName]}`;
}

module.exports = { compile };
```

## Update Task

- **Parse Component Files**
    
    - Implement a parser to read&nbsp;`.comp.html`&nbsp;files and extract HTML, script, and style sections.
    - Identify expressions within attributes and text nodes using a pattern like&nbsp;`{expression}`.
    
- **Generate Class Structure**
    
    - Create a class for each component with a constructor that initializes default values.
    - Attach a shadow DOM to encapsulate styles and markup.
    
- **Build DOM Programmatically**
    
    - Convert the extracted HTML into JavaScript code that creates DOM elements using&nbsp;`document.createElement`.
    - Use&nbsp;`document.createDocumentFragment`&nbsp;for efficient initial rendering.
    - Cache node references for elements containing reactive expressions.
    
- **Create Getters and Setters**
    
    - For each reactive expression, generate a getter and setter in the class.
    - Ensure setters update the DOM directly using cached node references.
    - Use template literals for updating attributes and text content dynamically.
    
- **Implement Event Listeners**
    
    - Parse event listener definitions from the script section.
    - Attach event listeners to the appropriate DOM elements in the&nbsp;`connected and disconnect Callback`.

Compilation format sample
Component definition
```
<!-- greeting.comp.html -->
<greeting-card>
  <div class=&quot;card&quot;>
    <h1>Hello, {name}!</h1>
    <p>Today is {dayOfWeek}.</p>
    <button onclick=&quot;changeGreeting()&quot;>Change Greeting</button>
    <img src=&quot;{imageUrl}&quot; alt=&quot;Greeting Image&quot;>
  </div>
</greeting-card>

<script>
let name = 'Alice';
let dayOfWeek = 'Monday';
let imageUrl = 'https://example.com/image.jpg';

function changeGreeting() {
  name = 'Bob';
}
</script>

<style>
.card {
  border: 1px solid #ccc;
  padding: 10px;
}
</style>
```

Compiled version
```
class GreetingCard extends HTMLElement {
  constructor() {
    super();
    this._name = 'Alice';
    this._dayOfWeek = 'Monday';
    this._imageUrl = 'https://example.com/image.jpg';

    this.attachShadow({ mode: 'open' });
    this._buildDOM();
  }

  _buildDOM() {
    const fragment = document.createDocumentFragment();
    const style = document.createElement('style');
    style.textContent = `
      .card {
        border: 1px solid #ccc;
        padding: 10px;
      }
    `;
    fragment.appendChild(style);

    const card = document.createElement('div');
    card.className = 'card';

    this._h1Name = document.createElement('h1');
    this._h1Name.textContent = `Hello, ${this._name}!`;
    card.appendChild(this._h1Name);

    this._pDayOfWeek = document.createElement('p');
    this._pDayOfWeek.textContent = `Today is ${this._dayOfWeek}.`;
    card.appendChild(this._pDayOfWeek);

    this._button = document.createElement('button');
    this._button.textContent = 'Change Greeting';
    card.appendChild(this._button);

    this._img = document.createElement('img');
    this._img.setAttribute('src', `${this._imageUrl}`);
    this._img.alt = 'Greeting Image';
    card.appendChild(this._img);

    fragment.appendChild(card);
    this.shadowRoot.appendChild(fragment);
  }

  connectedCallback() {
    this._button.addEventListener('click', this.changeGreeting.bind(this));
  }

  disconnectedCallback() {
    this._button.removeEventListener('click', this.changeGreeting.bind(this));
  }

  get name() { return this._name; }
  set name(value) {
    if (this._name !== value) {
      this._name = value;
      this._h1Name.textContent = `Hello, ${value}!`;
    }
  }

  get dayOfWeek() { return this._dayOfWeek; }
  set dayOfWeek(value) {
    if (this._dayOfWeek !== value) {
      this._dayOfWeek = value;
      this._pDayOfWeek.textContent = `Today is ${value}.`;
    }
  }

  get imageUrl() { return this._imageUrl; }
  set imageUrl(value) {
    if (this._imageUrl !== value) {
      this._imageUrl = value;
      this._img.setAttribute('src', `${value}`);
    }
  }

  changeGreeting() {
    this.name = 'Bob';
  }
}

customElements.define('greeting-card', GreetingCard);
```

---

Follow the task requirements exactly, the compiled output must be exact, don't change anything and make sure of not breaking the other features of the compiler.

Avoid any type of .replace method, instead direct done manipulation based on the context, weather it's an attribute, text node, etc...

Stick to the defined task, don't lose the point.