Building a Sitecore XM Cloud Component Creator: My Second Hackathon Experience

A few weeks ago, I participated in my second Sitecore Hackathon with team "404 Bugs Not Found". Our mission? Create a VSCode extension that streamlines component creation and deployment in Sitecore XM Cloud. Here's a deep dive into our technical solution.

The Technical Challenge

Sitecore XM Cloud development involves several manual steps when creating components:

  • Creating the component's TSX/JSX file
  • Setting up the component's template in Sitecore
  • Creating placeholder settings
  • Configuring the component rendering
  • Linking everything together

We aimed to automate this entire process through a VSCode extension.

Core Technical Features

1. Interactive UI with WebView

We created a sophisticated form interface using VSCode's WebView API. Here's a snippet of our form structure:

class="form-group">
     for="componentName">Component Name
     required type="text" id="componentName" placeholder="Enter component name">


 class="form-group">
     for="componentType">Component Type
     id="componentType">
         value="none">None
         value="withDatasourceCheck">With Datasource Check
         value="withDatasourceRendering">With Datasource Rendering

2. Dynamic Field Management

One of our most interesting features is the dynamic field management system:

function addField() {
    const fieldsContainer = document.getElementById('fields');
    const fieldRow = document.createElement('div');
    fieldRow.className = 'field-row';
    fieldRow.innerHTML = `
        
        
            Single Line Text
            Rich Text Field
            Link Field
            Image Field
            Checkbox
            Multiline Text
        
        
            ${fieldCount}">
            ${fieldCount}">Required
        
        ×
    `;
    fieldsContainer.appendChild(fieldRow);
}

3. GraphQL Integration

The most challenging part was integrating with Sitecore's GraphQL API. Here's our template creation mutation:

mutation {
    createItemTemplate(
        input: {
            name: "${templateName}",
            parent: "${templateParent}",
            createStandardValuesItem: true,
            sections: {
                name: "Content",
                fields: [
                    {
                        name: "${field.label}",
                        type: "${field.value === "RichText" ? "Rich Text" : field.value}",
                        required: ${field.isRequired}
                    }
                ]
            }
        }
    ) {
        itemTemplate {
            templateId
            name
            ownFields {
                nodes {
                    name
                    type
                }
            }
        }
    }
}

4. Placeholder Management

We implemented a sophisticated placeholder system that handles both static and dynamic placeholders:

function updatePlaceholderKey(select) {
    const row = select.closest('.placeholder-row');
    const keyInput = row.querySelectorAll('input[type="text"]')[1];
    const currentKey = keyInput.value;

    if (select.value === 'dynamic') {
        if (!currentKey.endsWith('-{*}')) {
            keyInput.value = currentKey + '-{*}';
        }
    } else {
        if (currentKey.endsWith('-{*}')) {
            keyInput.value = currentKey.slice(0, -4);
        }
    }
}

5. Rendering Creation

The final piece was creating the rendering with proper placeholder references:

const renderingQuery = `
    mutation {
        createItem(
            input: {
                name: "${sitecoreName}",
                templateId: "{04646A89-996F-4EE7-878A-FFDBF1F0EF0D}",
                parent: "${renderingParent}",
                language: "en",
                fields: [
                    { name: "componentName", value: "${codeName}" }
                    ${placeholderIds.length > 0
                        ? `, { name: "Placeholders", value: "${placeholderIds.join("|")}" }`
                        : ""
                    }
                ]
            }
        ) {
            item {
                itemId
                name
                path
            }
        }
    }`;

Technical Challenges Overcome

  1. Field Type Mapping: We had to carefully map our field types to Sitecore's expected formats:
switch (field.value) {
    case "RichText":
        return "Rich Text";
    case "SingleLineText":
        return "Single-Line Text";
    case "MultilineText":
        return "Multi-Line Text";
    case "ImageField":
        return "Image";
    case "LinkField":
        return "General Link";
    case "Checkbox":
        return "Checkbox";
    default:
        return field.value;
}
  1. Placeholder Reference Management: Ensuring proper linking between placeholders and renderings required careful handling of IDs and proper mutation ordering.

  2. Type Safety: Implementing proper TypeScript interfaces and type checking throughout the extension.

Future Technical Enhancements

  1. Support for more complex field types and configurations
  2. Enhanced template inheritance capabilities
  3. Integration with Sitecore CLI
  4. Support for component variants
  5. Automated testing infrastructure

Conclusion

This hackathon pushed us to create a solution that not only works but is also maintainable and extensible. The combination of VSCode's WebView API, TypeScript's type safety, and Sitecore's GraphQL API allowed us to create a tool that significantly improves the developer experience.

The code is open source and available for the community to use and enhance. We're looking forward to seeing how it evolves and helps other Sitecore developers streamline their workflow.

Repository
Video Demo


Are you interested in contributing to this project? Check out our repository and let's make Sitecore development even better together!