132 lines
3.9 KiB
JavaScript
132 lines
3.9 KiB
JavaScript
const path = require('node:path');
|
|
const { execSync } = require('node:child_process');
|
|
const fs = require('../fs-native');
|
|
|
|
const BADGE_URL = 'https://bmad-badge.vercel.app';
|
|
const escapedBadgeUrl = BADGE_URL.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
const BADGE_PATTERN = new RegExp(`\\[!\\[BMAD\\]\\(${escapedBadgeUrl}/[^)]+\\)\\]\\(https://github\\.com/bmad-code-org/BMAD-METHOD\\)`);
|
|
const README_NAMES = ['README.md', 'readme.md', 'README', 'readme'];
|
|
|
|
/**
|
|
* Resolve owner and repo from the project's git remote origin URL.
|
|
* Supports HTTPS and SSH formats.
|
|
* @param {string} projectDir - Project root directory
|
|
* @returns {{ owner: string, repo: string } | null} Parsed owner/repo or null
|
|
*/
|
|
function resolveGitRemote(projectDir) {
|
|
try {
|
|
const raw = execSync('git remote get-url origin', {
|
|
cwd: projectDir,
|
|
encoding: 'utf8',
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
}).trim();
|
|
|
|
const httpsMatch = raw.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
if (httpsMatch) {
|
|
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
}
|
|
} catch {
|
|
// no git remote
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Find the first README file in the project directory.
|
|
* Checks common README naming variants (case-insensitive).
|
|
* @param {string} projectDir - Project root directory
|
|
* @returns {Promise<string | null>} Absolute path to README or null
|
|
*/
|
|
async function findReadme(projectDir) {
|
|
for (const name of README_NAMES) {
|
|
const fullPath = path.join(projectDir, name);
|
|
if (await fs.pathExists(fullPath)) {
|
|
return fullPath;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check whether the content already contains a BMAD badge.
|
|
* @param {string} content - README file content
|
|
* @returns {boolean} True if badge is present
|
|
*/
|
|
function hasBadge(content) {
|
|
return BADGE_PATTERN.test(content);
|
|
}
|
|
|
|
/**
|
|
* Generate the BMAD badge markdown line.
|
|
* @param {string} owner - Repository owner
|
|
* @param {string} repo - Repository name
|
|
* @returns {string} Badge markdown string
|
|
*/
|
|
function generateBadgeMarkdown(owner, repo) {
|
|
return `[](https://github.com/bmad-code-org/BMAD-METHOD)`;
|
|
}
|
|
|
|
/**
|
|
* Inject the BMAD badge into README content.
|
|
* Places the badge after the first heading, alongside any existing badges.
|
|
* @param {string} content - Original README content
|
|
* @param {string} owner - Repository owner
|
|
* @param {string} repo - Repository name
|
|
* @returns {string} Updated README content with badge
|
|
*/
|
|
function injectBadge(content, owner, repo) {
|
|
const badgeLine = generateBadgeMarkdown(owner, repo);
|
|
|
|
const lines = content.split('\n');
|
|
|
|
// Find the first heading (# title)
|
|
let headingEnd = 0;
|
|
for (const [i, line] of lines.entries()) {
|
|
headingEnd = i + 1;
|
|
if (line.startsWith('#')) break;
|
|
}
|
|
|
|
// Check if there are existing badges right after the heading
|
|
let insertAt = headingEnd;
|
|
while (insertAt < lines.length && /^\[!\[.*?\]\(.*?\)\]\(.*?\)/.test(lines[insertAt].trim())) {
|
|
insertAt++;
|
|
}
|
|
|
|
lines.splice(insertAt, 0, badgeLine);
|
|
return lines.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Remove the BMAD badge from README content.
|
|
* @param {string} content - README file content
|
|
* @returns {string} Cleaned README content without the badge line
|
|
*/
|
|
function removeBadge(content) {
|
|
return content
|
|
.split('\n')
|
|
.filter((line) => !BADGE_PATTERN.test(line.trim()))
|
|
.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Create a minimal README.md content with project heading and BMAD badge.
|
|
* @param {string} owner - Repository owner
|
|
* @param {string} repo - Repository name
|
|
* @param {string} projectName - Project name for the heading
|
|
* @returns {string} New README content
|
|
*/
|
|
function createReadmeWithBadge(owner, repo, projectName) {
|
|
const badgeLine = generateBadgeMarkdown(owner, repo);
|
|
return `# ${projectName}\n\n${badgeLine}\n`;
|
|
}
|
|
|
|
module.exports = {
|
|
resolveGitRemote,
|
|
findReadme,
|
|
hasBadge,
|
|
generateBadgeMarkdown,
|
|
injectBadge,
|
|
removeBadge,
|
|
createReadmeWithBadge,
|
|
};
|