
Using The Block Collection And Block Party
- 26 installs
- 45 repo stars
- Updated August 4, 2026
- adobe/helix-website
using-the-block-collection-and-block-party is a Claude Code skill that finds reference AEM Edge Delivery block implementations and patterns from Adobe's Block Collection and the community Block Party.
About
using-the-block-collection-and-block-party helps a developer find reference implementations, code snippets, and integration patterns for AEM Edge Delivery blocks. It searches Adobe's vetted Block Collection and the community-driven Block Party for existing blocks, plugins, and build tools to use as a starting point. A developer runs it while building a new block to reuse proven patterns.
- Finds reference block implementations and code patterns from Adobe's Block Collection and the community Block Party
- Runs search scripts over both repositories in parallel to surface carousels, accordions, plugins, and build tools
- Guides when to prefer vetted Block Collection blocks versus specialized Block Party contributions
Using The Block Collection And Block Party by the numbers
- 26 all-time installs (skills.sh)
- Ranked #1,494 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
using the block collection and block party capabilities & compatibility
- Capabilities
- block search · reference lookup · code patterns
- Works with
- github
- Use cases
- web design · frontend
What using the block collection and block party says it does
This skill helps you find reference implementations, code examples, and patterns from two key AEM Edge Delivery resources:
**Block Collection**: Adobe-maintained reference blocks following best practices
Block Party is the only source for sidekick plugins, build tools, and integrations
npx skills add https://github.com/adobe/helix-website --skill using-the-block-collection-and-block-partyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 26 |
|---|---|
| repo stars | ★ 45 |
| Last updated | August 4, 2026 |
| Repository | adobe/helix-website ↗ |
What it does
Find reference AEM Edge Delivery blocks and patterns from Block Collection and Block Party to reuse when building.
Who is it for?
AEM Edge Delivery developers looking for an existing block or pattern to start from before writing new code
Skip if: Reading official documentation (use docs-search) or minor CSS tweaks to existing code
When should I use this skill?
Building a new block and wanting to see if a similar implementation, plugin, or build tool already exists
What you get
Relevant reference blocks, plugins, or integration patterns to inform your block implementation
- Relevant reference block, plugin, or integration examples with source to review
By the numbers
- Searches two repositories (Block Collection and Block Party)
- Runs both search scripts in parallel for comprehensive results
Files
Using the Block Collection and Block Party
Overview
This skill helps you find reference implementations, code examples, and patterns from two key AEM Edge Delivery resources:
- Block Collection: Adobe-maintained reference blocks following best practices
- Block Party: Community-driven repository of blocks, plugins, tools, and integrations
Use the provided search scripts to discover relevant examples, then review the code to inform your implementation approach.
When to Use This Skill
Use this skill when:
- Building a new block and want to see if similar implementations exist
- Looking for code patterns or snippets to solve a specific problem
- Searching for integration examples (e.g., third-party services, build tools)
- Need reference implementations for sidekick or Document Authoring plugins
- Want to understand best practices through working examples
Do NOT use this skill when:
- You need official documentation (use
docs-searchinstead) - You're making minor CSS tweaks to existing code (just edit directly)
- You already know exactly which block/example you need (use it directly)
Related Skills
- building-blocks: This skill is called from building-blocks during development
- docs-search: Use for official aem.live documentation
- content-driven-development: Use when creating content models for blocks
Key Concepts
Block Collection vs Block Party
Block Collection (Prefer this when available)
- Maintained by Adobe
- Vetted for best practices
- Excellent content modeling
- High performance and accessibility standards
- Limited to commonly-needed blocks
- Documentation: https://www.aem.live/developer/block-collection
- Repository: https://github.com/adobe/aem-block-collection
- Live site: https://main--aem-block-collection--adobe.aem.live
Block Party (Use for specialized needs)
- Community-driven contributions
- Broader variety of content types
- Includes experimental/innovative approaches
- Only approved entries are returned by the search script
- Contains blocks, plugins, build tools, integrations, and more
- Documentation: https://www.aem.live/developer/block-party/
- Search index: https://www.aem.live/developer/block-party/block-party.json?sheet=curated-list-new
When to prefer which:
- Start with Block Collection for standard blocks (carousels, accordions, cards, etc.)
- Use Block Party when Block Collection doesn't have what you need
- Block Party is the only source for sidekick plugins, build tools, and integrations
- Sometimes Block Party has innovative approaches worth considering even if Block Collection has a similar block
How to Use This Skill
Step 1: Identify Search Terms
Determine what you're looking for and identify relevant search terms. Think about similar or alternative names for the functionality.
Examples:
- Looking for FAQ block → search for "faq" AND "accordion" (Block Collection has accordion)
- Looking for image gallery → search for "gallery", "carousel", "slideshow"
- Looking for navigation → search for "navigation", "menu", "header"
- Looking for build tooling → search for "webpack", "vite", "sass", "typescript"
Good search terms:
- Specific functionality names: "carousel", "tabs", "modal"
- Tool names: "sass", "webpack", "target"
- Component types: "navigation", "footer", "hero"
Poor search terms:
- Too generic: "content", "page", "website"
- Too specific: "my-custom-carousel-with-auto-play"
Step 2: Search Block Collection
IMPORTANT: Run BOTH search scripts in parallel for comprehensive results:
# Run both searches in parallel (preferred approach)
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js <search-term> & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js <search-term> & \
waitWhy use both scripts:
search-block-collection-github.js- Searches actual repository folders via GitHub API (most comprehensive)search-block-collection.js- Searches navigation page (provides display names and catches edge cases)- Running both ensures maximum coverage and catches blocks that might be missed by either approach alone
Examples:
# Search for accordion/FAQ blocks (both scripts)
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js accordion & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js accordion & \
wait
# Search for embed block (both scripts)
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js embed & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js embed & \
wait
# If running both is problematic, prioritize the GitHub API version
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js carouselStep 3: Search Block Party
Execute the Block Party search script from the project root:
node .claude/skills/block-collection-and-party/scripts/search-block-party.js [--category <category>] <search-term> [additional-terms...]Options:
--category <category>: Filter by specific category (Block, Sidekick Plugin, DA Plugin, Code Snippet, Build Tooling, etc.)- Without
--category: Searches all categories
Examples:
# Search for breadcrumb blocks
node .claude/skills/block-collection-and-party/scripts/search-block-party.js breadcrumb
# Search for Sass integration examples
node .claude/skills/block-collection-and-party/scripts/search-block-party.js sass
# Search only for build tooling
node .claude/skills/block-collection-and-party/scripts/search-block-party.js --category "Build Tooling" webpack
# Multi-word search
node .claude/skills/block-collection-and-party/scripts/search-block-party.js adobe target integrationStep 4: Review Search Results
Block Collection Results (type: "block"):
{
"query": "accordion",
"source": "Adobe AEM Block Collection",
"totalItems": 26,
"matchCount": 1,
"results": [
{
"name": "accordion",
"displayName": "Accordion",
"type": "block",
"liveExampleUrl": "https://main--aem-block-collection--adobe.aem.live/block-collection/accordion",
"jsUrl": "https://github.com/adobe/aem-block-collection/blob/main/blocks/accordion/accordion.js",
"cssUrl": "https://github.com/adobe/aem-block-collection/blob/main/blocks/accordion/accordion.css"
}
]
}Block Collection Results (type: "default-content"):
{
"query": "breadcrumb",
"source": "Adobe AEM Block Collection",
"totalItems": 26,
"matchCount": 1,
"results": [
{
"name": "breadcrumbs",
"displayName": "Breadcrumbs",
"type": "default-content",
"liveExampleUrl": "https://main--aem-block-collection--adobe.aem.live/block-collection/breadcrumbs",
"note": "This is default content documentation, not a standalone block. Code may be part of other blocks (e.g., breadcrumbs are in the header block). Visit https://www.aem.live/developer/block-collection and the live example URL for implementation guidance.",
"documentationUrl": "https://www.aem.live/developer/block-collection"
}
]
}Block Party Results:
{
"query": "breadcrumb",
"category": "All categories",
"source": "AEM Block Party (Approved Only)",
"totalEntries": 90,
"approvedEntries": 62,
"matchCount": 1,
"results": [
{
"title": "Breadcrumbs",
"category": "Block",
"description": "A breadcrumb navigation component...",
"githubUrl": "https://github.com/...",
"showcaseUrl": "https://...",
"githubProfile": "https://github.com/..."
}
]
}Step 5: Get Block Structure Examples (CRITICAL for HTML generation)
IMPORTANT: Before writing any HTML for a block, ALWAYS fetch the pre-decoration structure examples first.
node .claude/skills/block-collection-and-party/scripts/get-block-structure.js <block-name>Why this is critical:
- Shows the exact HTML structure the block expects BEFORE JavaScript decoration
- Reveals the row/column pattern (e.g., each card is a row with 2 columns: image | content)
- Displays multiple variants (e.g., "Cards" vs "Cards (no images)")
- Prevents HTML structure mistakes that cause blocks to fail decoration
Examples:
# Get accordion structure
node .claude/skills/block-collection-and-party/scripts/get-block-structure.js accordion
# Get cards structure (will show multiple variants)
node .claude/skills/block-collection-and-party/scripts/get-block-structure.js cards
# Get tabs structure
node .claude/skills/block-collection-and-party/scripts/get-block-structure.js tabsOutput includes:
- Block description and source code URL
- All available variants with their names
- Pre-decoration HTML for each variant (simplified, without image optimization noise)
- Structural analysis (rows, columns, content types per column)
When to use:
- ✅ Before generating HTML for page migration
- ✅ Before writing block content in HTML files
- ✅ When block decoration is failing (verify your HTML matches expected structure)
- ✅ When uncertain about content model (e.g., "Is each card a row or all cards in one row?")
This step prevents the most common mistake: Writing incorrect HTML structure that doesn't match what the block's JavaScript decoration expects.
Step 6: Examine the Code
Use the provided URLs to review the implementation:
For Block Collection results with `type: "block"`: 1. FIRST: Get block structure examples (Step 5) to understand the expected HTML 2. Read the JS file to understand decoration logic 3. Read the CSS file to see styling approach 4. Visit the live example URL to see the block in action
For Block Collection results with `type: "default-content"`: 1. These represent standard HTML elements and patterns (breadcrumbs, buttons, headings, etc.) 2. Code exists but may be part of other blocks (e.g., breadcrumbs code is in the header block) 3. Visit the documentationUrl (https://www.aem.live/developer/block-collection) to find implementation details 4. Visit the liveExampleUrl to see examples and understand how to author the content 5. Search the Block Collection repository for related blocks that might contain the implementation
For Block Party entries: 1. Visit the GitHub URL to see the code 2. Visit the showcase URL to see it in action (if available) 3. Review the description to understand the purpose and approach
Step 7: Apply Learnings
Use the reference implementations to inform your approach:
- Understand the content model used
- Study decoration patterns and techniques
- Review CSS architecture and responsive approaches
- Adapt (don't copy) the code to fit your specific needs
- Ensure you follow your project's coding standards
Search Behavior Details
Block Collection Search
- Searches block folder names in the GitHub repository
- Returns exact and partial matches (case-insensitive)
- Provides direct links to JS, CSS, and live examples
- Fast and reliable (limited to ~16 blocks)
Block Party Search
- Searches title, description, and category fields
- Supports category filtering
- Returns all matching entries (not limited)
- Shows approval status for each entry
- Includes diverse content types beyond blocks
Examples
Example 1: Building an FAQ Block
User Request: "I need to build an FAQ section with expandable questions"
Good Approach: 1. Recognize FAQ often uses accordion pattern 2. Search Block Collection with both scripts:
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js accordion & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js accordion & \
wait3. Review results from both searches (they should align, but running both ensures nothing is missed) 4. Find the accordion block with JS, CSS, and live example URLs 5. Review the implementation approach 6. Adapt the pattern to your specific FAQ needs
Why this works:
- Used alternative term "accordion" for "FAQ"
- Started with Block Collection (Adobe best practices)
- Ran both search scripts for comprehensive coverage
- Found a vetted, accessible, performant implementation
Example 2: Finding Breadcrumb Implementation
User Request: "Add breadcrumb navigation to the site"
Good Approach: 1. Search Block Collection first with both scripts:
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js breadcrumb & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js breadcrumb & \
wait2. Find that breadcrumbs is "default-content" (not a standalone block) 3. Search Block Party: node .claude/skills/block-collection-and-party/scripts/search-block-party.js breadcrumb 4. Find breadcrumb block in Block Party 5. Review the implementation, noting it's community-contributed 6. Evaluate if it meets your needs or needs adaptation
Why this works:
- Checked Block Collection first with both scripts (best practices)
- Discovered breadcrumbs exist in Block Collection but as default content (part of header block)
- Fell back to Block Party for standalone implementation
- Aware that Block Party code may need more review
Example 3: Integrating Sass
User Request: "Can we use Sass for our styles instead of plain CSS?"
Good Approach: 1. Recognize this is a build tooling question (not a block) 2. Skip Block Collection (doesn't have build tools) 3. Search Block Party: node .claude/skills/block-collection-and-party/scripts/search-block-party.js --category "Build Tooling" sass 4. Find Sass integration examples 5. Review the approach and adapt to your project
Why this works:
- Recognized Block Party is the right resource for build tools
- Used category filter to narrow results
- Found community examples of the integration
Example 4: Multiple Implementations Exist
User Request: "Build a carousel for product images"
Scenario: Both Block Collection and Block Party have carousel implementations
Good Approach: 1. Search Block Collection with both scripts:
node .claude/skills/block-collection-and-party/scripts/search-block-collection-github.js carousel & \
node .claude/skills/block-collection-and-party/scripts/search-block-collection.js carousel & \
wait2. Find Block Collection carousel from both search results 3. Also search Block Party: node .claude/skills/block-collection-and-party/scripts/search-block-party.js carousel 4. Find multiple Block Party carousels 5. Prefer Block Collection for best practices 6. Review Block Party versions to see if they have innovative features worth considering 7. Make informed decision based on requirements
Why this works:
- Searched Block Collection with both scripts for comprehensive coverage
- Searched Block Party to see all options
- Defaulted to Block Collection (Adobe vetted)
- Considered Block Party for potential innovations
- Made an informed decision rather than blindly copying
Important Reminders
1. Always search for alternative names - "FAQ" = "accordion", "slideshow" = "carousel" 2. Prefer Block Collection when available - it's vetted for quality and best practices 3. Use Block Party for specialized needs - it has broader variety but needs more evaluation 4. Don't copy blindly - understand the code and adapt it to your project 5. Review content models carefully - how authors structure content is critical 6. Check accessibility and performance - especially for Block Party code 7. Search both resources - sometimes both have implementations with different trade-offs 8. Category matters for Block Party - use filters when you know what type you need
Common Search Patterns
| Need | Block Collection Search | Block Party Search |
|---|---|---|
| FAQ section | accordion | faq, accordion |
| Image gallery | carousel | gallery, carousel, slideshow |
| Tabbed content | tabs | tabs, tabbed |
| Navigation | header | navigation, menu, header |
| Footer | footer | footer |
| Product cards | cards | cards, product |
| Video embed | video, embed | video, embed, youtube |
| Build tools | N/A | Use --category "Build Tooling" |
| Sidekick plugins | N/A | Use --category "Sidekick Plugin" |
| Integrations | N/A | Search for service name (e.g., target, analytics) |
Troubleshooting
No results from both Block Collection scripts:
- Running both scripts ensures comprehensive coverage
- If neither script returns results, the block likely doesn't exist in Block Collection
- Try alternative search terms (e.g., "embed" vs "video", "faq" vs "accordion")
- Fall back to Block Party search
- If user insists the block exists, use WebFetch to manually check:
https://github.com/adobe/aem-block-collection/tree/main/blocks- Consider building from scratch with guidance from
building-blocksskill
Different results between the two scripts:
- This is normal - the GitHub API script searches folder names, the nav script searches the navigation
- Both results are valid - review both to ensure you haven't missed anything
- Prefer GitHub API results if there's a discrepancy (it's more direct)
IMPORTANT - When search returns no results but block likely exists:
- Don't immediately accept "no results" as definitive
- Running both scripts maximizes chances of finding existing blocks
- If the user suggests a block should exist, investigate further
- Common blocks that may exist: embed, video, form, consent-management
- Use WebFetch to manually browse the GitHub repo
- Cross-reference with blocks you know exist (like video, accordion, carousel)
Too many results in Block Party:
- Use
--categoryto filter - Refine search terms to be more specific
- Review descriptions to find best matches
Found code but seems outdated:
- Check Block Collection for newer patterns
- Review official docs with
docs-searchskill - Consider using as inspiration but implementing with modern approaches
Multiple implementations, unsure which to use:
- Prefer Block Collection for standard functionality
- Choose Block Party for specialized or innovative features
- Consider your specific requirements (performance, accessibility, features)
- Review code quality and documentation before deciding
#!/usr/bin/env node
/*
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/**
* Fetch and parse block structure examples from Adobe Block Collection
*
* This script retrieves the .plain.html version of a block's documentation page,
* which contains pre-decoration HTML structure examples showing the expected
* content model for the block.
*
* Usage:
* node get-block-structure.js <block-name>
* node get-block-structure.js accordion
* node get-block-structure.js cards
*
* Output: JSON with block metadata, variants, and HTML structure examples
*/
import { JSDOM } from 'jsdom';
const BLOCK_COLLECTION_BASE = 'https://main--aem-block-collection--adobe.aem.live';
/**
* Simplify HTML by removing optimized picture elements and showing essential structure
*/
function simplifyHTML(html) {
// Remove picture optimization attributes that clutter the output
let simplified = html
.replace(/\s+srcset="[^"]*"/g, '')
.replace(/\s+type="[^"]*"/g, '')
.replace(/\s+media="[^"]*"/g, '')
.replace(/\s+loading="[^"]*"/g, '')
.replace(/\s+width="[^"]*"/g, '')
.replace(/\s+height="[^"]*"/g, '')
.replace(/\?width=[^"'\s]*/g, ''); // Remove image optimization params
// Replace multi-line picture elements with simplified version
simplified = simplified.replace(
/<picture>\s*(<source[^>]*>\s*)*\s*<img\s+([^>]*?)src="([^"]*)"([^>]*?)>\s*<\/picture>/gs,
(match, sources, beforeSrc, src, afterSrc) => {
const altMatch = match.match(/alt="([^"]*)"/);
const alt = altMatch ? ` alt="${altMatch[1]}"` : '';
return `<picture><img src="${src}"${alt}></picture>`;
}
);
// Compact whitespace but preserve structure
simplified = simplified
.replace(/>\s+</g, '>\n<')
.replace(/\n\s*\n/g, '\n')
.trim();
return simplified;
}
/**
* Analyze HTML structure to provide a human-readable description
*/
function analyzeStructure(html, blockName) {
const dom = new JSDOM(html);
const doc = dom.window.document;
const block = doc.querySelector(`.${blockName}`);
if (!block) return 'Structure analysis unavailable';
const rows = Array.from(block.children);
const analysis = [];
analysis.push(`Block has ${rows.length} row(s)`);
rows.forEach((row, i) => {
const cols = Array.from(row.children);
const colDescriptions = cols.map(col => {
const elements = [];
if (col.querySelector('picture')) elements.push('image');
if (col.querySelector('h1, h2, h3, h4, h5, h6')) elements.push('heading');
if (col.querySelector('p')) elements.push('paragraph(s)');
if (col.querySelector('ul, ol')) elements.push('list');
if (col.querySelector('a')) elements.push('link(s)');
return elements.length > 0 ? elements.join(', ') : 'content';
});
analysis.push(` Row ${i + 1}: ${cols.length} column(s) [${colDescriptions.join(' | ')}]`);
});
return analysis.join('\n');
}
/**
* Fetch and parse block structure from Block Collection
*/
async function getBlockStructure(blockName) {
const url = `${BLOCK_COLLECTION_BASE}/block-collection/${blockName}.plain.html`;
try {
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) {
return {
success: false,
error: `Block "${blockName}" not found in Block Collection`,
url,
suggestion: 'Use search-block-collection-github.js to find available blocks'
};
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const html = await response.text();
const dom = new JSDOM(html);
const doc = dom.window.document;
// Find all sections (top-level divs in body)
const sections = Array.from(doc.body.children);
const variants = [];
let description = null;
let sourceCodeUrl = null;
// Process each section
sections.forEach(section => {
const metadata = section.querySelector('.library-metadata');
if (metadata) {
// Extract metadata
const metadataRows = Array.from(metadata.children);
const metadataObj = {};
metadataRows.forEach(row => {
const cells = Array.from(row.children);
if (cells.length === 2) {
const key = cells[0].textContent.trim();
const value = cells[1].textContent.trim();
metadataObj[key] = value;
// Extract source code link if present
const link = cells[1].querySelector('a[href*="github.com"]');
if (link) {
sourceCodeUrl = link.href;
}
}
});
// Store description (usually in last metadata block)
if (metadataObj.description) {
description = metadataObj.description.replace(/\s*Source Code\s*$/, '').trim();
}
// Find the block content (div with class matching block name)
const blockDiv = section.querySelector(`[class*="${blockName}"]`);
if (blockDiv && metadataObj.name) {
const blockHTML = blockDiv.outerHTML;
const simplifiedHTML = simplifyHTML(blockHTML);
const structure = analyzeStructure(blockHTML, blockName);
variants.push({
name: metadataObj.name,
html: simplifiedHTML,
structure
});
}
}
});
if (variants.length === 0) {
return {
success: false,
error: `No block examples found in ${url}`,
url
};
}
return {
success: true,
blockName,
url,
description,
sourceCodeUrl,
liveExampleUrl: `${BLOCK_COLLECTION_BASE}/block-collection/${blockName}`,
variants,
totalVariants: variants.length,
usage: {
purpose: 'Pre-decoration HTML structure examples',
note: 'This shows the HTML structure BEFORE JavaScript decoration. Use this to understand the expected content model when authoring content or generating HTML.'
}
};
} catch (error) {
return {
success: false,
error: error.message,
url,
blockName
};
}
}
// CLI execution
async function main() {
const blockName = process.argv[2];
if (!blockName) {
console.error(JSON.stringify({
success: false,
error: 'Missing block name argument',
usage: 'node get-block-structure.js <block-name>',
examples: [
'node get-block-structure.js accordion',
'node get-block-structure.js cards',
'node get-block-structure.js tabs'
]
}, null, 2));
process.exit(1);
}
const result = await getBlockStructure(blockName);
console.log(JSON.stringify(result, null, 2));
if (!result.success) {
process.exit(1);
}
}
// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
{
"type": "module"
}
#!/usr/bin/env node
/**
* Search the Adobe AEM Block Collection for blocks matching a search term
* This version uses GitHub API to get actual block folders
*
* Usage: node search-block-collection-github.js <search-term>
*
* Example: node search-block-collection-github.js accordion
*/
import https from 'https';
// Block Collection constants
const REPO_OWNER = 'adobe';
const REPO_NAME = 'aem-block-collection';
const BLOCKS_PATH = 'blocks';
const REPO_BASE_URL = `https://github.com/${REPO_OWNER}/${REPO_NAME}`;
const SITE_BASE_URL = 'https://main--aem-block-collection--adobe.aem.live';
const API_URL = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/contents/${BLOCKS_PATH}`;
/**
* Fetch JSON from a URL using HTTPS
*/
function fetchUrl(url) {
return new Promise((resolve, reject) => {
https.get(url, {
headers: {
'User-Agent': 'AEM-Block-Search-Script',
'Accept': 'application/vnd.github.v3+json'
}
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`Failed to parse JSON: ${e.message}`));
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
}
});
}).on('error', (err) => {
reject(err);
});
});
}
/**
* Get all block folders from the GitHub repository
*/
async function getBlocks() {
try {
const contents = await fetchUrl(API_URL);
// Filter for directories only
return contents
.filter(item => item.type === 'dir')
.map(item => ({
name: item.name,
displayName: item.name.split('-').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ')
}));
} catch (error) {
throw new Error(`Failed to fetch Block Collection from GitHub: ${error.message}`);
}
}
/**
* Determine if a block is "Default Content" (no block code) or a "Sample Block"
*/
function isDefaultContent(blockName) {
// Default Content items don't have block implementations
const defaultContentItems = [
'breadcrumbs', 'buttons', 'code', 'headings', 'icons', 'images',
'links', 'lists', 'metadata', 'section-metadata', 'sections', 'text'
];
return defaultContentItems.includes(blockName.toLowerCase());
}
/**
* Search for blocks matching the search term
*/
function searchBlocks(blocks, searchTerm) {
const lowerSearchTerm = searchTerm.toLowerCase();
return blocks
.filter(block => {
// Search in both the URL slug and display name
return block.name.toLowerCase().includes(lowerSearchTerm) ||
block.displayName.toLowerCase().includes(lowerSearchTerm);
})
.map(block => {
const result = {
name: block.name,
displayName: block.displayName,
type: isDefaultContent(block.name) ? 'default-content' : 'block',
liveExampleUrl: `${SITE_BASE_URL}/block-collection/${block.name}`
};
// Only add code URLs for actual blocks (not default content)
if (result.type === 'block') {
result.jsUrl = `${REPO_BASE_URL}/blob/main/${BLOCKS_PATH}/${block.name}/${block.name}.js`;
result.cssUrl = `${REPO_BASE_URL}/blob/main/${BLOCKS_PATH}/${block.name}/${block.name}.css`;
} else {
result.note = 'This is default content documentation, not a standalone block. Code may be part of other blocks (e.g., breadcrumbs are in the header block). Visit https://www.aem.live/developer/block-collection and the live example URL for implementation guidance.';
result.documentationUrl = 'https://www.aem.live/developer/block-collection';
}
return result;
});
}
/**
* Main function
*/
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node search-block-collection-github.js <search-term>');
console.error('Example: node search-block-collection-github.js accordion');
process.exit(1);
}
const searchTerm = args[0];
try {
// Fetch all blocks from GitHub API
const blocks = await getBlocks();
// Search for matching blocks
const results = searchBlocks(blocks, searchTerm);
// Output results as JSON
const output = {
query: searchTerm,
source: 'Adobe AEM Block Collection (via GitHub API)',
repository: `${REPO_BASE_URL}`,
totalItems: blocks.length,
matchCount: results.length,
results: results
};
console.log(JSON.stringify(output, null, 2));
// Exit with code 0 if results found, 1 if no results
process.exit(results.length > 0 ? 0 : 1);
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
// Run the script
main();
#!/usr/bin/env node
/**
* Search the Adobe AEM Block Collection for blocks matching a search term
*
* Usage: node search-block-collection.js <search-term>
*
* Example: node search-block-collection.js accordion
*/
import https from 'https';
// Block Collection constants
const REPO_OWNER = 'adobe';
const REPO_NAME = 'aem-block-collection';
const BLOCKS_PATH = 'blocks';
const REPO_BASE_URL = `https://github.com/${REPO_OWNER}/${REPO_NAME}`;
const SITE_BASE_URL = 'https://main--aem-block-collection--adobe.aem.live';
const NAV_URL = `${SITE_BASE_URL}/nav.plain.html`;
/**
* Fetch HTML/text from a URL using HTTPS
*/
function fetchUrl(url, parseJson = false) {
return new Promise((resolve, reject) => {
https.get(url, {
headers: {
'User-Agent': 'AEM-Block-Search-Script'
}
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
if (parseJson) {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`Failed to parse JSON: ${e.message}`));
}
} else {
resolve(data);
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
}
});
}).on('error', (err) => {
reject(err);
});
});
}
/**
* Parse nav.plain.html to extract block names
*/
function parseBlocksFromNav(html) {
const blocks = [];
// Match all <a href="/block-collection/{block-name}">Display Name</a>
const linkPattern = /<a href="\/block-collection\/([^"]+)">([^<]+)<\/a>/g;
let match;
while ((match = linkPattern.exec(html)) !== null) {
const urlSlug = match[1];
const displayName = match[2];
blocks.push({
name: urlSlug,
displayName: displayName
});
}
return blocks;
}
/**
* Get all blocks from the Block Collection navigation
*/
async function getBlocks() {
try {
const html = await fetchUrl(NAV_URL, false);
return parseBlocksFromNav(html);
} catch (error) {
throw new Error(`Failed to fetch Block Collection navigation: ${error.message}`);
}
}
/**
* Determine if a block is "Default Content" (no block code) or a "Sample Block"
*/
function isDefaultContent(blockName) {
// Default Content items don't have block implementations
const defaultContentItems = [
'breadcrumbs', 'buttons', 'code', 'headings', 'icons', 'images',
'links', 'lists', 'metadata', 'section-metadata', 'sections', 'text'
];
return defaultContentItems.includes(blockName.toLowerCase());
}
/**
* Search for blocks matching the search term
*/
function searchBlocks(blocks, searchTerm) {
const lowerSearchTerm = searchTerm.toLowerCase();
return blocks
.filter(block => {
// Search in both the URL slug and display name
return block.name.toLowerCase().includes(lowerSearchTerm) ||
block.displayName.toLowerCase().includes(lowerSearchTerm);
})
.map(block => {
const result = {
name: block.name,
displayName: block.displayName,
type: isDefaultContent(block.name) ? 'default-content' : 'block',
liveExampleUrl: `${SITE_BASE_URL}/block-collection/${block.name}`
};
// Only add code URLs for actual blocks (not default content)
if (result.type === 'block') {
result.jsUrl = `${REPO_BASE_URL}/blob/main/${BLOCKS_PATH}/${block.name}/${block.name}.js`;
result.cssUrl = `${REPO_BASE_URL}/blob/main/${BLOCKS_PATH}/${block.name}/${block.name}.css`;
} else {
result.note = 'This is default content documentation, not a standalone block. Code may be part of other blocks (e.g., breadcrumbs are in the header block). Visit https://www.aem.live/developer/block-collection and the live example URL for implementation guidance.';
result.documentationUrl = 'https://www.aem.live/developer/block-collection';
}
return result;
});
}
/**
* Main function
*/
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node search-block-collection.js <search-term>');
console.error('Example: node search-block-collection.js accordion');
process.exit(1);
}
const searchTerm = args[0];
try {
// Fetch all blocks from navigation
const blocks = await getBlocks();
// Search for matching blocks
const results = searchBlocks(blocks, searchTerm);
// Output results as JSON
const output = {
query: searchTerm,
source: 'Adobe AEM Block Collection',
repository: `${REPO_BASE_URL}`,
totalItems: blocks.length,
matchCount: results.length,
results: results
};
console.log(JSON.stringify(output, null, 2));
// Exit with code 0 if results found, 1 if no results
process.exit(results.length > 0 ? 0 : 1);
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
// Run the script
main();
#!/usr/bin/env node
/**
* Search the AEM Block Party index for blocks, plugins, tools, and integrations
*
* Note: Only searches approved entries from the Block Party index
*
* Usage: node search-block-party.js [--category <category>] <search-term> [additional-terms...]
*
* Examples:
* node search-block-party.js breadcrumb
* node search-block-party.js --category "Build Tooling" sass
* node search-block-party.js adobe target integration
*/
import https from 'https';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Block Party constants
const BLOCK_PARTY_INDEX_URL = 'https://www.aem.live/developer/block-party/block-party.json?sheet=curated-list-new';
const CACHE_DIR = path.join(__dirname, '..', '.cache');
const CACHE_FILE = path.join(CACHE_DIR, 'block-party-index.json');
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
/**
* Fetch data from a URL using HTTPS
*/
function fetchUrl(url) {
return new Promise((resolve, reject) => {
https.get(url, {
headers: {
'User-Agent': 'AEM-Block-Party-Search-Script'
}
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
try {
resolve(JSON.parse(data));
} catch (e) {
reject(new Error(`Failed to parse JSON: ${e.message}`));
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
}
});
}).on('error', (err) => {
reject(err);
});
});
}
/**
* Check if cached index is still valid
*/
function isCacheValid() {
if (!fs.existsSync(CACHE_FILE)) {
return false;
}
const stats = fs.statSync(CACHE_FILE);
const age = Date.now() - stats.mtimeMs;
return age < CACHE_TTL;
}
/**
* Load index from cache
*/
function loadFromCache() {
try {
const data = fs.readFileSync(CACHE_FILE, 'utf8');
return JSON.parse(data);
} catch (error) {
return null;
}
}
/**
* Save index to cache
*/
function saveToCache(data) {
try {
if (!fs.existsSync(CACHE_DIR)) {
fs.mkdirSync(CACHE_DIR, { recursive: true });
}
fs.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2), 'utf8');
} catch (error) {
// Silently fail - caching is not critical
console.error('Warning: Failed to save cache:', error.message);
}
}
/**
* Fetch the Block Party index (with caching)
*/
async function getBlockPartyIndex() {
// Try cache first
if (isCacheValid()) {
const cached = loadFromCache();
if (cached) {
return cached;
}
}
// Fetch fresh data
try {
const data = await fetchUrl(BLOCK_PARTY_INDEX_URL);
saveToCache(data);
return data;
} catch (error) {
throw new Error(`Failed to fetch Block Party index: ${error.message}`);
}
}
/**
* Get all unique categories from entries (case-insensitive, preserving first occurrence)
*/
function getUniqueCategories(entries) {
const categoryMap = new Map(); // lowercase -> original case
entries.forEach(entry => {
if (entry.category && entry.category.trim()) {
const trimmed = entry.category.trim();
const lower = trimmed.toLowerCase();
if (!categoryMap.has(lower)) {
categoryMap.set(lower, trimmed);
}
}
});
return Array.from(categoryMap.values()).sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase())
);
}
/**
* Validate category against available categories
*/
function validateCategory(category, availableCategories) {
if (!category) {
return { valid: true };
}
const lowerCategory = category.toLowerCase();
const matchingCategory = availableCategories.find(cat =>
cat.toLowerCase() === lowerCategory || cat.toLowerCase().includes(lowerCategory)
);
if (!matchingCategory) {
return {
valid: false,
error: `Category "${category}" not found.`,
availableCategories
};
}
return { valid: true };
}
/**
* Filter entries by category
*/
function filterByCategory(entries, category) {
if (!category) {
return entries;
}
const lowerCategory = category.toLowerCase();
return entries.filter(entry => {
const entryCategory = (entry.category || '').toLowerCase();
return entryCategory.includes(lowerCategory);
});
}
/**
* Search entries by search terms
*/
function searchEntries(entries, searchTerms) {
if (searchTerms.length === 0) {
return entries;
}
return entries.filter(entry => {
const searchableText = [
entry.title || '',
entry.description || '',
entry.category || '',
entry.githubProfile || ''
].join(' ').toLowerCase();
// Match all search terms (AND logic)
return searchTerms.every(term =>
searchableText.includes(term.toLowerCase())
);
});
}
/**
* Format results for output
*/
function formatResults(entries) {
return entries.map(entry => ({
title: entry.title || 'Untitled',
category: entry.category || 'Unknown',
description: entry.description || '',
githubUrl: entry.githubUrl || '',
showcaseUrl: entry.showcaseUrl || '',
githubProfile: entry.githubProfile || ''
}));
}
/**
* Parse command line arguments
*/
function parseArgs(args) {
const result = {
category: null,
searchTerms: []
};
for (let i = 0; i < args.length; i++) {
if (args[i] === '--category') {
if (i + 1 < args.length) {
result.category = args[i + 1];
i++; // Skip the next argument
} else {
throw new Error('--category flag requires a value');
}
} else {
result.searchTerms.push(args[i]);
}
}
return result;
}
/**
* Main function
*/
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node search-block-party.js [--category <category>] <search-term> [additional-terms...]');
console.error('');
console.error('Examples:');
console.error(' node search-block-party.js breadcrumb');
console.error(' node search-block-party.js --category "Build Tooling" sass');
console.error(' node search-block-party.js adobe target integration');
console.error('');
console.error('Common categories:');
console.error(' - Block');
console.error(' - Sidekick Plugin');
console.error(' - DA Plugin');
console.error(' - Code Snippet');
console.error(' - Build Tooling');
process.exit(1);
}
try {
// Parse arguments
const { category, searchTerms } = parseArgs(args);
// Fetch the index
const indexData = await getBlockPartyIndex();
const allEntries = indexData.data || [];
// Filter to only approved entries
const approvedEntries = allEntries.filter(entry =>
entry.approved === 'Yes' || entry.approved === true || entry.approved === 'true'
);
// Get available categories (from approved entries only)
const availableCategories = getUniqueCategories(approvedEntries);
// Validate category if provided
const validation = validateCategory(category, availableCategories);
if (!validation.valid) {
console.error('Error:', validation.error);
console.error('');
console.error('Available categories:');
validation.availableCategories.forEach(cat => {
console.error(` - ${cat}`);
});
process.exit(1);
}
// Filter by category if specified (using approved entries only)
let entries = filterByCategory(approvedEntries, category);
// Search by terms
entries = searchEntries(entries, searchTerms);
// Format results
const results = formatResults(entries);
// Output results as JSON
const output = {
query: searchTerms.join(' '),
category: category || 'All categories',
source: 'AEM Block Party (Approved Only)',
indexUrl: BLOCK_PARTY_INDEX_URL,
totalEntries: allEntries.length,
approvedEntries: approvedEntries.length,
matchCount: results.length,
results: results
};
console.log(JSON.stringify(output, null, 2));
// Exit with code 0 if results found, 1 if no results
process.exit(results.length > 0 ? 0 : 1);
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
// Run the script
main();