UNPKG

@supernovaio/cli

Version:

Supernova.io Command Line Interface

311 lines (309 loc) 13.4 kB
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2ba5291d-6e75-5d3c-9139-40ca7af32e6e")}catch(e){}}(); import fs from "node:fs"; import path from "node:path"; export function folderNameToDisplayName(folderName) { return folderName .replaceAll(/[-_]/g, " ") .replaceAll(/([A-Z])/g, " $1") .trim() .replaceAll(/\s+/g, " ") .replaceAll(/\b\w/g, c => c.toUpperCase()); } const filesToIgnore = ["README.md", ".DS_Store"]; function getAllFilesInDirectory(dirPath, relativeTo) { return fs .readdirSync(dirPath, { recursive: true, withFileTypes: true }) .filter(entry => entry.isFile() && !filesToIgnore.includes(entry.name)) .map(entry => path.relative(relativeTo, path.join(entry.parentPath || dirPath, entry.name))) .sort(); } function findThumbnail(templateDir) { return [".png", ".svg", ".jpg", ".jpeg"] .map(ext => `thumbnail${ext}`) .find(filename => fs.existsSync(path.join(templateDir, filename))); } function parseDescriptionFromReadme(readmeFilePath) { if (!fs.existsSync(readmeFilePath)) return ""; try { const content = fs.readFileSync(readmeFilePath, "utf8"); const match = content.match(/^#[^\n]*\n+([^\n#]+)/m); if (!match) return ""; let description = match[1].trim(); description = description.replaceAll("**", "").replaceAll("*", ""); description = description.charAt(0).toUpperCase() + description.slice(1); if (!description.endsWith(".") && !description.endsWith("!") && !description.endsWith("?")) { description += "."; } return description; } catch { return ""; } } function parseImportsFromTSFile(filePath, baseDir) { if (!fs.existsSync(filePath)) return []; try { const content = fs.readFileSync(filePath, "utf8"); const dependencies = []; const importRegex = /import\s+(?:[\w*{}\s,]+\s+from\s+)?['"`]([^'"`]+)['"`]/g; let match; while ((match = importRegex.exec(content)) !== null) { const importPath = match[1]; if (importPath.startsWith("./") || importPath.startsWith("../")) { try { const fileDir = path.dirname(filePath); const resolvedPath = path.resolve(fileDir, importPath); const withoutJs = resolvedPath.endsWith(".js") ? resolvedPath.slice(0, -3) : null; const possiblePaths = [ resolvedPath, resolvedPath + ".tsx", resolvedPath + ".ts", resolvedPath + ".json", resolvedPath + ".js", ...(withoutJs ? [withoutJs + ".ts", withoutJs + ".tsx", withoutJs] : []), ]; const actualPath = possiblePaths.find(p => fs.existsSync(p)); if (actualPath) { const containerRoot = path.resolve(baseDir, "../../.."); const relativePath = path.relative(containerRoot, actualPath).replaceAll("\\", "/"); if (relativePath.startsWith("supernova/")) { dependencies.push(relativePath); } } } catch { } } } return dependencies; } catch { return []; } } function parseImportsFromDirectory(itemDir) { const dependencies = new Set(); try { const entries = fs.readdirSync(itemDir, { recursive: true, withFileTypes: true }); for (const entry of entries) { if (entry.isFile()) { const ext = path.extname(entry.name).toLowerCase(); if (ext === ".ts" || ext === ".tsx") { const fullPath = path.join(entry.parentPath || itemDir, entry.name); const fileDeps = parseImportsFromTSFile(fullPath, itemDir); for (const dep of fileDeps) dependencies.add(dep); } } } } catch { } return [...dependencies].sort(); } function parseMainReadmeTable(mainReadmePath, itemKey) { if (!fs.existsSync(mainReadmePath)) return null; try { const content = fs.readFileSync(mainReadmePath, "utf8"); const tableMatch = content.match(/(\|\s*(?:Pattern|Template)\s*\|[^\n]*)\n\|[-\s|]*\n((?:\|[^\n]*\n)*)/i); if (!tableMatch) return null; const headerCells = tableMatch[1] .split("|") .map(cell => cell.trim()) .filter(Boolean); const nameIndex = headerCells.findIndex(cell => /^(Pattern|Template)$/i.test(cell)); const descIndex = headerCells.findIndex(cell => /^Description$/i.test(cell)); if (nameIndex === -1 || descIndex === -1) return null; const rows = tableMatch[2].split("\n").filter(row => row.trim()); for (const row of rows) { const cells = row .split("|") .map(cell => cell.trim()) .filter(Boolean); if (cells.length > Math.max(nameIndex, descIndex)) { const name = cells[nameIndex]?.replace(/\[([^\]]+)\].*/, "$1"); const description = cells[descIndex]; if (name === itemKey) { return description.endsWith(".") ? description : description + "."; } } } } catch { } return null; } function extractDescriptionFromReadme(itemDir, fallbackName, itemKey, subdirectory) { const itemReadmePath = path.join(itemDir, "README.md"); const individualDescription = parseDescriptionFromReadme(itemReadmePath); if (individualDescription) { return individualDescription; } const containerRoot = path.resolve(itemDir, "../../.."); const mainReadmePath = path.join(containerRoot, "supernova", subdirectory, "README.md"); const tableDescription = parseMainReadmeTable(mainReadmePath, itemKey); if (tableDescription) { return tableDescription; } return `Use this to prototype similar to ${fallbackName}`; } function readPackageJson(basePath) { const packageJsonPath = path.join(basePath, "package.json"); if (!fs.existsSync(packageJsonPath)) { throw new Error(`package.json not found at ${packageJsonPath}`); } const content = fs.readFileSync(packageJsonPath, "utf8"); return JSON.parse(content); } function writePackageJson(basePath, packageData) { const packageJsonPath = path.join(basePath, "package.json"); const content = JSON.stringify(packageData, null, 2) + "\n"; fs.writeFileSync(packageJsonPath, content, "utf8"); } function discoverItems(basePath, subdirectory) { const resolvedBasePath = basePath || process.cwd(); const itemsDir = path.join(resolvedBasePath, "supernova", subdirectory); if (!fs.existsSync(itemsDir)) { return {}; } const items = {}; const entries = fs.readdirSync(itemsDir, { withFileTypes: true }); for (const entry of entries) { if (!entry.isDirectory() || entry.name === "README.md") { continue; } const itemKey = entry.name; const itemDir = path.join(itemsDir, itemKey); const displayName = folderNameToDisplayName(itemKey); const description = extractDescriptionFromReadme(itemDir, displayName, itemKey, subdirectory); const thumbnail = findThumbnail(itemDir); const files = getAllFilesInDirectory(itemDir, itemDir); const containerRoot = path.resolve(itemDir, "../../.."); const itemRelativePath = path.relative(containerRoot, itemDir).replaceAll("\\", "/"); const fullPathFiles = files.map(file => `${itemRelativePath}/${file}`); const importDependencies = parseImportsFromDirectory(itemDir); const allDependencies = [...new Set(importDependencies)]; const itemBasePath = itemRelativePath; const externalDependencies = allDependencies.filter(dep => !dep.startsWith(itemBasePath + "/")); const validExternalDependencies = externalDependencies.filter(dep => { const fullPath = path.join(containerRoot, dep); const exists = fs.existsSync(fullPath); if (!exists) { console.warn(`Warning: Dependency not found: ${dep} (expected at ${fullPath})`); } return exists; }); const allFiles = new Set([...fullPathFiles, ...validExternalDependencies]); const filteredFiles = [...allFiles].filter(file => !file.endsWith("README.md") && !["/thumbnail.png", "/thumbnail.svg", "/thumbnail.jpg", "/thumbnail.jpeg"].some(ext => file.endsWith(ext))); const existingFiles = filteredFiles .filter(file => { const fullPath = path.join(containerRoot, file); const exists = fs.existsSync(fullPath); if (!exists) { console.warn(`Warning: File not found: ${file} (expected at ${fullPath})`); } return exists; }) .sort(); const itemInfo = { name: displayName, description, files: existingFiles, }; if (thumbnail) { itemInfo.thumbnail = `${itemRelativePath}/${thumbnail}`; } items[itemKey] = itemInfo; } return items; } function discoverTemplates(basePath) { const resolvedBasePath = basePath || process.cwd(); return discoverItems(resolvedBasePath, "templates"); } function discoverPatterns(basePath) { const resolvedBasePath = basePath || process.cwd(); return discoverItems(resolvedBasePath, "patterns"); } function resolveTransitiveDependencies(items, patterns, basePath) { const resolvedItems = {}; for (const [itemKey, itemInfo] of Object.entries(items)) { const resolvedFiles = new Set(itemInfo.files); const dependentPatterns = new Set(); for (const file of itemInfo.files) { const pathMatch = file.match(/^supernova\/patterns\/([^\/]+)\//); if (pathMatch) { dependentPatterns.add(pathMatch[1]); } } function addTransitiveDependencies(patternKey, visited = new Set()) { if (visited.has(patternKey)) { return; } visited.add(patternKey); const pattern = patterns[patternKey]; if (!pattern) { return; } for (const file of pattern.files) resolvedFiles.add(file); for (const file of pattern.files) { const pathMatch = file.match(/^supernova\/patterns\/([^\/]+)\//); if (pathMatch && pathMatch[1] !== patternKey) { addTransitiveDependencies(pathMatch[1], new Set(visited)); } } } for (const patternKey of dependentPatterns) { addTransitiveDependencies(patternKey); } const containerRoot = path.resolve(basePath); const validFiles = [...resolvedFiles] .filter(file => { const fullPath = path.join(containerRoot, file); const exists = fs.existsSync(fullPath); if (!exists) { console.warn(`Warning: Transitive dependency not found: ${file} (expected at ${fullPath})`); } return exists; }) .sort(); resolvedItems[itemKey] = { ...itemInfo, files: validFiles, }; } return resolvedItems; } export async function discoverTemplatesAndPatterns(basePath) { const resolvedBasePath = basePath || process.cwd(); const rawTemplates = discoverTemplates(resolvedBasePath); const rawPatterns = discoverPatterns(resolvedBasePath); const templates = resolveTransitiveDependencies(rawTemplates, rawPatterns, resolvedBasePath); const patterns = resolveTransitiveDependencies(rawPatterns, rawPatterns, resolvedBasePath); return { templates, patterns }; } export async function discoverFilesForTemplates(pkgTemplates, basePath) { const { templates } = await discoverTemplatesAndPatterns(basePath); return Object.fromEntries(Object.entries(pkgTemplates).map(([key, pkgTemplate]) => [key, { ...templates[key], ...pkgTemplate }])); } export async function discoverAndUpdatePackageJson(basePath) { const result = await discoverTemplatesAndPatterns(basePath); const resolvedBasePath = basePath || process.cwd(); const packageData = readPackageJson(resolvedBasePath); if (!packageData.supernova) { packageData.supernova = {}; } packageData.supernova.templates = Object.fromEntries(Object.entries(result.templates).map(([key, { name, description }]) => [key, { name, description }])); writePackageJson(resolvedBasePath, packageData); return result; } //# sourceMappingURL=discover.js.map //# debugId=2ba5291d-6e75-5d3c-9139-40ca7af32e6e