add AI generated script
add gitignore for automatically generated stuff
This commit is contained in:
@@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function findWorkspaceRoot(startDir) {
|
||||
let current = startDir;
|
||||
while (true) {
|
||||
if (fs.existsSync(path.join(current, 'workspace.yml'))) {
|
||||
return current;
|
||||
}
|
||||
const parent = path.dirname(current);
|
||||
if (parent === current) {
|
||||
throw new Error('workspace.yml not found from the provided start directory');
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function readText(filePath) {
|
||||
return fs.readFileSync(filePath, 'utf8');
|
||||
}
|
||||
|
||||
function stripQuotes(value) {
|
||||
const trimmed = String(value).trim();
|
||||
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
||||
return trimmed.slice(1, -1);
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function parseWorkspace(workspacePath) {
|
||||
const text = readText(workspacePath);
|
||||
const lines = text.split(/\r?\n/);
|
||||
const collections = [];
|
||||
let inCollections = false;
|
||||
let currentCollection = null;
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!inCollections && trimmed === 'collections:') {
|
||||
inCollections = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inCollections) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line.startsWith(' ') && !line.startsWith('\t') && trimmed) {
|
||||
break;
|
||||
}
|
||||
|
||||
const nameMatch = line.match(/^\s*-\s+name:\s*(.+)$/);
|
||||
if (nameMatch) {
|
||||
currentCollection = { name: stripQuotes(nameMatch[1]) };
|
||||
collections.push(currentCollection);
|
||||
continue;
|
||||
}
|
||||
|
||||
const pathMatch = line.match(/^\s*path:\s*(.+)$/);
|
||||
if (pathMatch && currentCollection) {
|
||||
currentCollection.path = stripQuotes(pathMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return { collections };
|
||||
}
|
||||
|
||||
function sanitizeVarName(value) {
|
||||
return String(value)
|
||||
.trim()
|
||||
.replace(/[{}]/g, '')
|
||||
.replace(/[^A-Za-z0-9_]/g, '_')
|
||||
.replace(/^([0-9])/, '_$1') || 'value';
|
||||
}
|
||||
|
||||
function collectPlaceholders(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return [];
|
||||
}
|
||||
const placeholders = [];
|
||||
const regex = /\{\{([^{}]+)\}\}/g;
|
||||
let match;
|
||||
while ((match = regex.exec(value)) !== null) {
|
||||
placeholders.push(match[1]);
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
function findBlock(lines, keyName) {
|
||||
for (let i = 0; i < lines.length; i += 1) {
|
||||
const trimmed = lines[i].trim();
|
||||
if (trimmed !== keyName && !trimmed.startsWith(`${keyName}:`)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const lineIndent = lines[i].match(/^\s*/)[0].length;
|
||||
const block = [];
|
||||
for (let j = i + 1; j < lines.length; j += 1) {
|
||||
const currentLine = lines[j];
|
||||
const currentTrimmed = currentLine.trim();
|
||||
if (!currentTrimmed) {
|
||||
block.push(currentLine);
|
||||
continue;
|
||||
}
|
||||
const currentIndent = currentLine.match(/^\s*/)[0].length;
|
||||
if (currentIndent <= lineIndent && !currentLine.startsWith(' ')) {
|
||||
break;
|
||||
}
|
||||
if (currentIndent <= lineIndent && currentTrimmed.startsWith('#')) {
|
||||
block.push(currentLine);
|
||||
continue;
|
||||
}
|
||||
if (currentIndent <= lineIndent) {
|
||||
break;
|
||||
}
|
||||
block.push(currentLine);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function parseRequestInfo(text) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const infoLines = findBlock(lines, 'info');
|
||||
const nameMatch = infoLines.join('\n').match(/^\s*name:\s*(.+)$/m);
|
||||
return nameMatch ? stripQuotes(nameMatch[1]) : '';
|
||||
}
|
||||
|
||||
function parseHttpBlock(text) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const blockLines = findBlock(lines, 'http');
|
||||
const blockText = blockLines.join('\n');
|
||||
const methodMatch = blockText.match(/^\s*method:\s*(.+)$/m);
|
||||
const urlMatch = blockText.match(/^\s*url:\s*(.+)$/m);
|
||||
|
||||
const params = [];
|
||||
const paramLines = blockLines.join('\n').split(/\r?\n/);
|
||||
let inParamsBlock = false;
|
||||
let paramsIndent = 0;
|
||||
let currentParam = null;
|
||||
|
||||
for (const line of paramLines) {
|
||||
const trimmed = line.trim();
|
||||
if (!inParamsBlock) {
|
||||
if (trimmed === 'params:') {
|
||||
inParamsBlock = true;
|
||||
paramsIndent = line.match(/^\s*/)[0].length;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!trimmed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const indent = line.match(/^\s*/)[0].length;
|
||||
if (indent <= paramsIndent) {
|
||||
break;
|
||||
}
|
||||
|
||||
const listItemMatch = line.match(/^\s*-\s+name:\s*(.+)$/);
|
||||
if (listItemMatch) {
|
||||
currentParam = { name: stripQuotes(listItemMatch[1]) };
|
||||
params.push(currentParam);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!currentParam) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const propertyMatch = line.match(/^\s*([A-Za-z0-9_]+):\s*(.+)$/);
|
||||
if (propertyMatch) {
|
||||
const [, key, value] = propertyMatch;
|
||||
currentParam[key] = stripQuotes(value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
method: methodMatch ? stripQuotes(methodMatch[1]) : 'GET',
|
||||
url: urlMatch ? stripQuotes(urlMatch[1]) : '',
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
function buildRequestContent(request, requestName) {
|
||||
const lines = [];
|
||||
const placeholders = new Set();
|
||||
|
||||
const addPlaceholders = (value) => {
|
||||
for (const placeholder of collectPlaceholders(value)) {
|
||||
placeholders.add(placeholder);
|
||||
}
|
||||
};
|
||||
|
||||
let url = request.url || '';
|
||||
if (url) {
|
||||
addPlaceholders(url);
|
||||
url = url.replace(/:([A-Za-z0-9_]+)/g, (_, name) => `{{${name}}}`);
|
||||
}
|
||||
|
||||
const queryParams = [];
|
||||
const headers = [];
|
||||
for (const param of request.params || []) {
|
||||
const name = param.name || '';
|
||||
const value = param.value || '';
|
||||
const type = (param.type || 'query').toLowerCase();
|
||||
const disabled = String(param.disabled).toLowerCase() === 'true';
|
||||
|
||||
if (disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addPlaceholders(value);
|
||||
if (type === 'header') {
|
||||
headers.push({ name, value });
|
||||
} else {
|
||||
queryParams.push({ name, value });
|
||||
}
|
||||
}
|
||||
|
||||
if (placeholders.size > 0) {
|
||||
lines.push(`# Variables for ${requestName}`);
|
||||
for (const placeholder of [...placeholders].sort()) {
|
||||
lines.push(`@${sanitizeVarName(placeholder)} = YOUR_VALUE_HERE`);
|
||||
}
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
const method = (request.method || 'GET').toUpperCase();
|
||||
let requestUrl = url;
|
||||
for (const param of queryParams) {
|
||||
if (!param.name) {
|
||||
continue;
|
||||
}
|
||||
const separator = requestUrl.includes('?') ? '&' : '?';
|
||||
requestUrl = `${requestUrl}${separator}${param.name}=${param.value}`;
|
||||
}
|
||||
|
||||
lines.push(`${method} ${requestUrl}`);
|
||||
for (const header of headers) {
|
||||
lines.push(`${header.name}: ${header.value}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function ensureDir(dirPath) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
function walkYamlFiles(rootDir) {
|
||||
const results = [];
|
||||
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||
continue;
|
||||
}
|
||||
const fullPath = path.join(rootDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
results.push(...walkYamlFiles(fullPath));
|
||||
} else if (entry.isFile() && /\.ya?ml$/i.test(entry.name)) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const workspaceRoot = findWorkspaceRoot(__dirname);
|
||||
const workspaceFile = path.join(workspaceRoot, 'workspace.yml');
|
||||
const workspace = parseWorkspace(workspaceFile);
|
||||
const collections = Array.isArray(workspace.collections) ? workspace.collections : [];
|
||||
|
||||
if (collections.length === 0) {
|
||||
throw new Error('No collections found in workspace.yml');
|
||||
}
|
||||
|
||||
for (const collection of collections) {
|
||||
if (!collection || !collection.name || !collection.path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceDir = path.join(workspaceRoot, collection.path);
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
console.warn(`Skipping missing collection path: ${collection.path}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const outputRoot = path.join(workspaceRoot, 'autodocs', 'http', collection.name);
|
||||
ensureDir(outputRoot);
|
||||
|
||||
const yamlFiles = walkYamlFiles(sourceDir);
|
||||
let processed = 0;
|
||||
|
||||
for (const yamlFile of yamlFiles) {
|
||||
const content = readText(yamlFile);
|
||||
const requestName = parseRequestInfo(content) || path.basename(yamlFile, path.extname(yamlFile));
|
||||
const httpBlock = parseHttpBlock(content);
|
||||
if (!httpBlock || !httpBlock.url) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const relativePath = path.relative(sourceDir, yamlFile);
|
||||
const parsedPath = path.parse(relativePath);
|
||||
const targetDir = path.join(outputRoot, parsedPath.dir);
|
||||
ensureDir(targetDir);
|
||||
|
||||
const outputFile = path.join(targetDir, `${parsedPath.name}.http`);
|
||||
const requestContent = buildRequestContent(httpBlock, requestName);
|
||||
fs.writeFileSync(outputFile, `${requestContent}\n`, 'utf8');
|
||||
processed += 1;
|
||||
}
|
||||
|
||||
console.log(`Generated ${processed} .http file(s) for ${collection.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user