remove node tools
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
function Initialize-PowerShellEnvironment {
|
||||
Write-Host "Initializing PowerShell environment..." -ForegroundColor Yellow
|
||||
|
||||
# Add any environment setup logic here, such as importing modules, setting variables, etc.
|
||||
# Example: Import-Module SomeModule
|
||||
Import-Module powershell-yaml -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "PowerShell environment initialized." -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Initialize-GenerateTools {
|
||||
Write-Host "Initializing Node.js dependencies..." -ForegroundColor Yellow
|
||||
|
||||
Push-Location -StackName NodeTools (Join-Path $PSScriptRoot "../tools/")
|
||||
|
||||
Get-childitem -Path . -Directory | ForEach-Object {
|
||||
Write-Host "Initializing $($_.Name)..." -ForegroundColor Cyan
|
||||
|
||||
Push-Location -StackName NodeTools $_.FullName
|
||||
try {
|
||||
# Install Node.js dependencies
|
||||
& npm install
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "npm install failed with exit code $LASTEXITCODE."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "npm install failed for $($_.Name): $_"
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
Pop-Location -StackName NodeTools
|
||||
}
|
||||
}
|
||||
|
||||
Pop-Location -StackName NodeTools
|
||||
|
||||
Write-Host "Node.js dependencies initialized." -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Invoke-Main {
|
||||
Write-Host "Preparing environment..." -ForegroundColor Cyan
|
||||
Write-Host
|
||||
|
||||
Initialize-PowerShellEnvironment
|
||||
Write-Host
|
||||
|
||||
Initialize-GenerateTools
|
||||
Write-Host
|
||||
|
||||
Write-Host "Environment preparation complete!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Invoke-Main
|
||||
@@ -0,0 +1,21 @@
|
||||
function Initialize-PowerShellEnvironment {
|
||||
Write-Host "Initializing PowerShell environment..." -ForegroundColor Yellow
|
||||
|
||||
# Add any environment setup logic here, such as importing modules, setting variables, etc.
|
||||
# Example: Import-Module SomeModule
|
||||
Import-Module powershell-yaml -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "PowerShell environment initialized." -ForegroundColor Green
|
||||
}
|
||||
|
||||
function Invoke-Main {
|
||||
Write-Host "Preparing environment..." -ForegroundColor Cyan
|
||||
Write-Host
|
||||
|
||||
Initialize-PowerShellEnvironment
|
||||
Write-Host
|
||||
|
||||
Write-Host "Environment preparation complete!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Invoke-Main
|
||||
@@ -6,7 +6,7 @@ cd /D "%~dp0"
|
||||
|
||||
echo.
|
||||
|
||||
@pwsh -NoProfile -ExecutionPolicy Bypass -Command .\scripts\setup-environment.ps1 %*
|
||||
@pwsh -NoProfile -ExecutionPolicy Bypass -Command .\scripts\setup-tools.ps1 %*
|
||||
|
||||
echo.
|
||||
|
||||
@@ -1,844 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import YAML from 'yaml';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const interpolationVariableRegex = /^{{(.*?)}}$/
|
||||
const DEFAULT_VAR_VALUE = 'EDIT_VALUE_HERE'
|
||||
const VARIABLE_NAME_VALUE_SEPARATOR = '='
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
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 parseYaml(filePath) {
|
||||
try {
|
||||
const text = fs.readFileSync(filePath, 'utf8');
|
||||
const parsed = YAML.parse(text);
|
||||
return parsed || {};
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse YAML file ${filePath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
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 parsePlaceholderContent(content) {
|
||||
const trimmed = String(content).trim();
|
||||
const dotenvMatch = trimmed.match(/^\$dotenv\s+(.+)$/i);
|
||||
if (dotenvMatch) {
|
||||
return { name: dotenvMatch[1].trim(), isDotenv: true };
|
||||
}
|
||||
return { name: trimmed, isDotenv: false };
|
||||
}
|
||||
|
||||
function collectPlaceholders(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return [];
|
||||
}
|
||||
const placeholders = [];
|
||||
const regex = /\{\{([^{}]+)\}\}/g;
|
||||
let match;
|
||||
while ((match = regex.exec(value)) !== null) {
|
||||
placeholders.push(parsePlaceholderContent(match[1]).name);
|
||||
}
|
||||
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 parsed = YAML.parse(text) || {};
|
||||
const http = parsed.http || {};
|
||||
const headers = [];
|
||||
|
||||
for (const header of Array.isArray(http.headers) ? http.headers : []) {
|
||||
if (!header || typeof header !== 'object') {
|
||||
continue;
|
||||
}
|
||||
headers.push({
|
||||
name: String(header.name || '').trim(),
|
||||
value: String(header.value ?? '').trim(),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
method: http.method || 'GET',
|
||||
url: http.url || '',
|
||||
params: Array.isArray(http.params) ? http.params : [],
|
||||
headers,
|
||||
body: http.body && typeof http.body === 'object' ? http.body : null,
|
||||
};
|
||||
}
|
||||
|
||||
function formatVariableValue(value, renderContext = {}) {
|
||||
if (value === null || value === undefined) {
|
||||
return "''";
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (value.trim() === '') {
|
||||
return "''";
|
||||
}
|
||||
const renderedValue = renderContext.renderValue ? renderContext.renderValue(value) : value;
|
||||
if (/\s/.test(renderedValue)) {
|
||||
return `"${renderedValue.replace(/"/g, '\\"')}"`;
|
||||
}
|
||||
return renderedValue;
|
||||
}
|
||||
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
export function mergeRequestConfig(base, updates) {
|
||||
if (!updates || typeof updates !== 'object') {
|
||||
return base;
|
||||
}
|
||||
|
||||
const merged = { ...(base || {}) };
|
||||
if (Object.prototype.hasOwnProperty.call(updates, 'auth')) {
|
||||
if (updates.auth === 'inherit') {
|
||||
if (merged.auth && typeof merged.auth === 'object') {
|
||||
merged.auth = merged.auth;
|
||||
} else {
|
||||
delete merged.auth;
|
||||
}
|
||||
} else if (typeof updates.auth === 'object' && updates.auth !== null) {
|
||||
merged.auth = updates.auth;
|
||||
} else {
|
||||
delete merged.auth;
|
||||
}
|
||||
} else {
|
||||
delete merged.auth;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(updates, 'headers')) {
|
||||
const headers = [...(Array.isArray(base.headers) ? base.headers : [])];
|
||||
const byName = new Map();
|
||||
for (const header of headers) {
|
||||
if (header && header.name) {
|
||||
byName.set(String(header.name), header);
|
||||
}
|
||||
}
|
||||
for (const header of updates.headers) {
|
||||
if (header && header.name) {
|
||||
byName.set(String(header.name), header);
|
||||
}
|
||||
}
|
||||
merged.headers = [...byName.values()];
|
||||
}
|
||||
|
||||
if (Array.isArray(updates.variables)) {
|
||||
const variables = [...(Array.isArray(base.variables) ? base.variables : [])];
|
||||
const byName = new Map();
|
||||
for (const variable of variables) {
|
||||
if (variable && variable.name) {
|
||||
byName.set(String(variable.name), variable);
|
||||
}
|
||||
}
|
||||
for (const variable of updates.variables) {
|
||||
if (variable && variable.name) {
|
||||
byName.set(String(variable.name), variable);
|
||||
}
|
||||
}
|
||||
merged.variables = [...byName.values()];
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
export function getRequestConfigForFile(yamlFile, sourceDir) {
|
||||
const resolved = [];
|
||||
const seenFiles = new Set();
|
||||
|
||||
const addFile = (filePath) => {
|
||||
if (!filePath || seenFiles.has(filePath)) {
|
||||
return;
|
||||
}
|
||||
seenFiles.add(filePath);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = parseYaml(filePath);
|
||||
let requestConfig = null;
|
||||
|
||||
if (parsed && parsed.request && typeof parsed.request === 'object') {
|
||||
requestConfig = parsed.request;
|
||||
} else if (parsed && typeof parsed === 'object') {
|
||||
const config = {};
|
||||
if (Object.prototype.hasOwnProperty.call(parsed, 'auth')) {
|
||||
config.auth = parsed.auth;
|
||||
} else if (parsed.http && typeof parsed.http === 'object' && Object.prototype.hasOwnProperty.call(parsed.http, 'auth')) {
|
||||
config.auth = parsed.http.auth;
|
||||
}
|
||||
if (Array.isArray(parsed.variables)) {
|
||||
config.variables = parsed.variables;
|
||||
}
|
||||
if (Object.keys(config).length > 0) {
|
||||
requestConfig = config;
|
||||
}
|
||||
}
|
||||
|
||||
if (requestConfig !== null) {
|
||||
resolved.push(requestConfig);
|
||||
} else if (path.resolve(filePath) === path.resolve(yamlFile)) {
|
||||
resolved.push({});
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore files that cannot be parsed as YAML for request inheritance.
|
||||
}
|
||||
};
|
||||
|
||||
const dirChain = [];
|
||||
let currentDir = path.dirname(yamlFile);
|
||||
while (true) {
|
||||
dirChain.unshift(currentDir);
|
||||
if (currentDir === sourceDir) {
|
||||
break;
|
||||
}
|
||||
const parentDir = path.dirname(currentDir);
|
||||
if (parentDir === currentDir) {
|
||||
break;
|
||||
}
|
||||
currentDir = parentDir;
|
||||
}
|
||||
|
||||
for (const dir of dirChain) {
|
||||
addFile(path.join(dir, 'opencollection.yml'));
|
||||
addFile(path.join(dir, 'folder.yml'));
|
||||
}
|
||||
|
||||
addFile(yamlFile);
|
||||
|
||||
return resolved.reduce((result, config) => mergeRequestConfig(result, config), {});
|
||||
}
|
||||
|
||||
|
||||
export function buildRequestContent(request, requestName, requestConfig = {}, dotenvVariables = new Set()) {
|
||||
const lines = [];
|
||||
const variableDefinitions = [];
|
||||
const commentedVariableDefinitions = [];
|
||||
const parameterVariableDefinitions = [];
|
||||
const seenVariables = new Set();
|
||||
|
||||
const addVariable = (name, value) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const normalized = String(name).trim();
|
||||
if (!normalized || seenVariables.has(normalized) || dotenvVariables.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
seenVariables.add(normalized);
|
||||
variableDefinitions.push({ name: normalized, value });
|
||||
};
|
||||
|
||||
const addParameterVariable = (name, value) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const normalized = String(name).trim();
|
||||
if (!normalized || seenVariables.has(normalized) || dotenvVariables.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
seenVariables.add(normalized);
|
||||
parameterVariableDefinitions.push({ name: normalized, value });
|
||||
};
|
||||
|
||||
const addCommentedVariable = (name, value) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const normalized = String(name).trim();
|
||||
if (!normalized) { // || seenVariables.has(normalized) || dotenvVariables.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
// seenVariables.add(normalized);
|
||||
commentedVariableDefinitions.push({ name: normalized, value });
|
||||
};
|
||||
|
||||
const addReferencedVariables = (value, fallbackValue = DEFAULT_VAR_VALUE) => {
|
||||
for (const placeholder of collectPlaceholders(String(value))) {
|
||||
addVariable(placeholder, fallbackValue);
|
||||
}
|
||||
};
|
||||
|
||||
const renderJsonValue = (value) => {
|
||||
if (typeof value === 'string') {
|
||||
return renderValue(value);
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => renderJsonValue(item));
|
||||
}
|
||||
if (value && typeof value === 'object') {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, nestedValue]) => [key, renderJsonValue(nestedValue)]),
|
||||
);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const addParameterVariables = (name, value) => {
|
||||
addParameterVariable(name, value);
|
||||
};
|
||||
|
||||
const addCommentedVariables = (name, value) => {
|
||||
addCommentedVariable(name, value);
|
||||
};
|
||||
|
||||
const renderValue = (value) => {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
return value.replace(/\{\{([^{}]+)\}\}/g, (match, inner) => {
|
||||
const placeholder = parsePlaceholderContent(inner);
|
||||
if (placeholder.isDotenv) {
|
||||
return match;
|
||||
}
|
||||
if (placeholder.name && dotenvVariables.has(placeholder.name)) {
|
||||
return `{{$dotenv ${placeholder.name}}}`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
};
|
||||
|
||||
const configVariables = Array.isArray(requestConfig.variables) ? requestConfig.variables : [];
|
||||
for (const variable of configVariables) {
|
||||
if (variable && variable.name) {
|
||||
addVariable(variable.name, variable.value);
|
||||
}
|
||||
}
|
||||
|
||||
let url = request.url || '';
|
||||
if (url) {
|
||||
addReferencedVariables(url);
|
||||
url = url.replace(/:([A-Za-z0-9_]+)/g, (_, name) => `{{${name}}}`);
|
||||
url = renderValue(url);
|
||||
// Since Bruno puts enabled params in the URL, this avoids duplicate query params
|
||||
url = url.split('?')[0];
|
||||
}
|
||||
|
||||
const queryParams = [];
|
||||
const headers = [];
|
||||
const addHeader = (name, value) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
addReferencedVariables(value ?? '');
|
||||
headers.push({ name: String(name).trim(), value: renderValue(value ?? '') });
|
||||
};
|
||||
|
||||
for (const header of Array.isArray(request.headers) ? request.headers : []) {
|
||||
if (!header || !header.name) {
|
||||
continue;
|
||||
}
|
||||
addHeader(header.name, header.value ?? '');
|
||||
}
|
||||
|
||||
for (const header of Array.isArray(requestConfig.headers) ? requestConfig.headers : []) {
|
||||
if (!header || !header.name) {
|
||||
continue;
|
||||
}
|
||||
addHeader(header.name, header.value ?? '');
|
||||
}
|
||||
|
||||
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) {
|
||||
addCommentedVariables(name, value);
|
||||
continue;
|
||||
}
|
||||
|
||||
addReferencedVariables(value);
|
||||
|
||||
if (type === 'header') {
|
||||
headers.push({ name, value: renderValue(value) });
|
||||
} else {
|
||||
queryParams.push({ name, value: `{{${name}}}` });
|
||||
addParameterVariables(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (requestConfig.auth) {
|
||||
if (requestConfig.auth.type === 'bearer') {
|
||||
addReferencedVariables(requestConfig.auth.token ?? '');
|
||||
addHeader('Authorization', `Bearer ${renderValue(requestConfig.auth.token ?? '')}`);
|
||||
} else if (requestConfig.auth.type === 'basic') {
|
||||
const username = requestConfig.auth.username ?? '';
|
||||
const password = requestConfig.auth.password ?? '';
|
||||
addReferencedVariables(username);
|
||||
addReferencedVariables(password);
|
||||
// VSCode REST Client can manage username:password format directly!
|
||||
addHeader('Authorization', `Basic ${renderValue(username)}:${renderValue(password)}`);
|
||||
} else {
|
||||
headers.push({
|
||||
name: `UNKNOWN_${requestConfig.auth.type}`,
|
||||
value: `Basic ${requestConfig.auth.token}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (commentedVariableDefinitions.length > 0) {
|
||||
lines.push(`# Other variables for ${requestName}`);
|
||||
for (const variable of commentedVariableDefinitions.sort((a, b) => {
|
||||
const nameComparison = a.name.localeCompare(b.name);
|
||||
return nameComparison !== 0 ? nameComparison : a.value.localeCompare(b.value);
|
||||
})) {
|
||||
lines.push(`# @${sanitizeVarName(variable.name)}${VARIABLE_NAME_VALUE_SEPARATOR}${formatVariableValue(variable.value, { renderValue })}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (parameterVariableDefinitions.length > 0) {
|
||||
lines.push(`# Parameter variables for ${requestName}`);
|
||||
for (const variable of parameterVariableDefinitions.sort((a, b) => {
|
||||
const nameComparison = a.name.localeCompare(b.name);
|
||||
return nameComparison !== 0 ? nameComparison : a.value.localeCompare(b.value);
|
||||
})) {
|
||||
lines.push(`@${sanitizeVarName(variable.name)}${VARIABLE_NAME_VALUE_SEPARATOR}${formatVariableValue(variable.value, { renderValue })}`);
|
||||
}
|
||||
}
|
||||
|
||||
let requestBody = '';
|
||||
if (request.body && typeof request.body === 'object') {
|
||||
const bodyType = String(request.body.type || '').toLowerCase();
|
||||
if (bodyType === 'json') {
|
||||
const jsonData = stripJsonComments(request.body.data, { whitespace: false });
|
||||
if (jsonData !== undefined && jsonData !== null) {
|
||||
if (typeof jsonData === 'string') {
|
||||
requestBody = renderValue(jsonData);
|
||||
} else {
|
||||
const renderedData = Array.isArray(jsonData)
|
||||
? jsonData.map((item) => renderJsonValue(item))
|
||||
: renderJsonValue(jsonData);
|
||||
requestBody = JSON.stringify(renderedData, null, 2);
|
||||
}
|
||||
}
|
||||
addHeader('Content-Type', 'application/json');
|
||||
} else if (bodyType === 'form-urlencoded') {
|
||||
const parts = [];
|
||||
for (const entry of Array.isArray(request.body.data) ? request.body.data : []) {
|
||||
if (!entry || !entry.name) {
|
||||
continue;
|
||||
}
|
||||
addReferencedVariables(entry.value ?? '');
|
||||
parts.push(`${entry.name}=${renderValue(entry.value ?? '')}`);
|
||||
}
|
||||
requestBody = parts.join('&');
|
||||
addHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
}
|
||||
}
|
||||
|
||||
if (variableDefinitions.length > 0) {
|
||||
lines.push(`# Variables for ${requestName}`);
|
||||
for (const variable of variableDefinitions.sort((a, b) => {
|
||||
const nameComparison = a.name.localeCompare(b.name);
|
||||
return nameComparison !== 0 ? nameComparison : a.value.localeCompare(b.value);
|
||||
})) {
|
||||
lines.push(`@${sanitizeVarName(variable.name)}${VARIABLE_NAME_VALUE_SEPARATOR}${formatVariableValue(variable.value, { renderValue })}`);
|
||||
}
|
||||
}
|
||||
|
||||
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('');
|
||||
lines.push(`${method} ${requestUrl}`);
|
||||
for (const header of headers) {
|
||||
lines.push(`${header.name}: ${header.value}`);
|
||||
}
|
||||
if (requestBody) {
|
||||
lines.push('');
|
||||
lines.push(requestBody);
|
||||
}
|
||||
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 getDotenvVariablesForTargetDir(targetDir, outputRoot, dotenvVariablesByTarget) {
|
||||
const variables = new Set();
|
||||
let currentDir = targetDir;
|
||||
|
||||
while (true) {
|
||||
const vars = dotenvVariablesByTarget.get(currentDir);
|
||||
if (vars) {
|
||||
for (const variable of vars) {
|
||||
variables.add(variable);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDir === outputRoot || path.dirname(currentDir) === currentDir) {
|
||||
break;
|
||||
}
|
||||
currentDir = path.dirname(currentDir);
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
function writeEnvironmentTemplates(sourceDir, outputRoot) {
|
||||
const targets = [];
|
||||
const dotenvVariablesByTarget = new Map();
|
||||
|
||||
const visit = (currentDir) => {
|
||||
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||
const hasEnvironmentsDir = entries.some((entry) => entry.isDirectory() && entry.name === 'environments' &&
|
||||
fs.readdirSync(path.join(currentDir, 'environments'), { withFileTypes: true }).some((envEntry) => envEntry.isFile() && /\.ya?ml$/i.test(envEntry.name)));
|
||||
|
||||
if (hasEnvironmentsDir) {
|
||||
targets.push(currentDir);
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||
continue;
|
||||
}
|
||||
visit(path.join(currentDir, entry.name));
|
||||
}
|
||||
};
|
||||
|
||||
visit(sourceDir);
|
||||
|
||||
for (const dir of targets) {
|
||||
const relativeDir = path.relative(sourceDir, dir);
|
||||
const targetDir = relativeDir && relativeDir !== '.' ? path.join(outputRoot, relativeDir) : outputRoot;
|
||||
ensureDir(targetDir);
|
||||
|
||||
const environmentsDir = path.join(dir, 'environments');
|
||||
if (!fs.existsSync(environmentsDir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const envFiles = fs.readdirSync(environmentsDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && /\.ya?ml$/i.test(entry.name))
|
||||
.map((entry) => path.join(environmentsDir, entry.name));
|
||||
|
||||
const variableNames = [];
|
||||
const seenNames = new Set();
|
||||
|
||||
for (const envFile of envFiles) {
|
||||
const parsed = parseYaml(envFile);
|
||||
const variables = Array.isArray(parsed.variables) ? parsed.variables : [];
|
||||
for (const variable of variables) {
|
||||
if (!variable || !variable.name) {
|
||||
continue;
|
||||
}
|
||||
const name = String(variable.name).trim();
|
||||
if (!name || seenNames.has(name)) {
|
||||
continue;
|
||||
}
|
||||
seenNames.add(name);
|
||||
variableNames.push(name);
|
||||
}
|
||||
}
|
||||
|
||||
const templateContent = variableNames.length > 0 ? `${variableNames.map(v => `${v}=${DEFAULT_VAR_VALUE}`).join('\n')}\n` : '';
|
||||
fs.writeFileSync(path.join(targetDir, '.env.template'), templateContent, 'utf8');
|
||||
dotenvVariablesByTarget.set(targetDir, new Set(variableNames));
|
||||
}
|
||||
|
||||
return dotenvVariablesByTarget;
|
||||
}
|
||||
|
||||
function writeJsFiles(sourceDir, outputRoot) {
|
||||
const targets = [];
|
||||
// const dotenvVariablesByTarget = new Map();
|
||||
|
||||
const visit = (currentDir) => {
|
||||
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||
const hasJsFiles = entries.some((entry) => entry.isFile() && /\.js$/i.test(entry.name));
|
||||
|
||||
if (hasJsFiles) {
|
||||
targets.push(currentDir);
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules') {
|
||||
continue;
|
||||
}
|
||||
visit(path.join(currentDir, entry.name));
|
||||
}
|
||||
};
|
||||
|
||||
visit(sourceDir);
|
||||
|
||||
for (const dir of targets) {
|
||||
const relativeDir = path.relative(sourceDir, dir);
|
||||
const targetDir = relativeDir && relativeDir !== '.' ? path.join(outputRoot, relativeDir) : outputRoot;
|
||||
ensureDir(targetDir);
|
||||
|
||||
const jsFileDir = dir;
|
||||
if (!fs.existsSync(jsFileDir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const jsFiles = fs.readdirSync(jsFileDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isFile() && /\.js$/i.test(entry.name))
|
||||
.map((entry) => [ path.join(jsFileDir, entry.name), path.join(targetDir, entry.name) ]);
|
||||
|
||||
// const variableNames = [];
|
||||
// const seenNames = new Set();
|
||||
|
||||
// for (const jsFile of jsFiles) {
|
||||
// const parsed = parseYaml(jsFile);
|
||||
// const variables = Array.isArray(parsed.variables) ? parsed.variables : [];
|
||||
// for (const variable of variables) {
|
||||
// if (!variable || !variable.name) {
|
||||
// continue;
|
||||
// }
|
||||
// const name = String(variable.name).trim();
|
||||
// if (!name || seenNames.has(name)) {
|
||||
// continue;
|
||||
// }
|
||||
// seenNames.add(name);
|
||||
// variableNames.push(name);
|
||||
// }
|
||||
// }
|
||||
|
||||
// const templateContent = variableNames.length > 0 ? `${variableNames.map(v => `${v}=${DEFAULT_ENV_VAR_VALUE}`).join('\n')}\n` : '';
|
||||
// fs.writeFileSync(path.join(targetDir, '.env.template'), templateContent, 'utf8');
|
||||
// dotenvVariablesByTarget.set(targetDir, new Set(variableNames));
|
||||
for (const jsFile of jsFiles) {
|
||||
fs.copyFileSync(jsFile[0], jsFile[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
function cleanFolder(dir) {
|
||||
if (!fs.existsSync(dir)) return;
|
||||
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
// Ricorsione
|
||||
cleanFolder(fullPath);
|
||||
|
||||
// Dopo la pulizia, se la cartella è vuota → cancellala
|
||||
const remaining = fs.readdirSync(fullPath);
|
||||
if (remaining.length === 0) {
|
||||
fs.rmdirSync(fullPath);
|
||||
}
|
||||
} else if (entry.isFile()) {
|
||||
// File da eliminare
|
||||
if (
|
||||
entry.name.endsWith(".js") ||
|
||||
entry.name.endsWith(".http") ||
|
||||
entry.name.endsWith(".env.template")
|
||||
) {
|
||||
fs.unlinkSync(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
const outputBaseRoot = path.join(workspaceRoot, 'autogen', 'httpyac_node');
|
||||
cleanFolder(outputBaseRoot);
|
||||
|
||||
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(outputBaseRoot, collection.name);
|
||||
ensureDir(outputRoot);
|
||||
|
||||
const writtenJsFiles = writeJsFiles(sourceDir, outputRoot);
|
||||
|
||||
const dotenvVariablesByTarget = writeEnvironmentTemplates(sourceDir, 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 requestConfig = getRequestConfigForFile(yamlFile, sourceDir);
|
||||
const outputFile = path.join(targetDir, `${parsedPath.name}.http`);
|
||||
const dotenvVariables = getDotenvVariablesForTargetDir(targetDir, outputRoot, dotenvVariablesByTarget);
|
||||
const requestContent = buildRequestContent(httpBlock, requestName, requestConfig, dotenvVariables);
|
||||
fs.writeFileSync(outputFile, `${requestContent}\n`, 'utf8');
|
||||
processed += 1;
|
||||
}
|
||||
|
||||
console.log(`${collection.name.padEnd(33)} => generated ${processed.toString().padStart(3)} .http file(s) and ${writtenJsFiles.length.toString().padStart(3)} .js file(s)`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { buildRequestContent, getRequestConfigForFile, mergeRequestConfig } from './generate-http-requests.js';
|
||||
|
||||
test('does not inherit parent auth when a child config has no auth override', () => {
|
||||
const parentAuth = { type: 'bearer', token: 'parent-token' };
|
||||
const merged = mergeRequestConfig({ auth: parentAuth }, {});
|
||||
|
||||
assert.equal(merged.auth, undefined);
|
||||
});
|
||||
|
||||
test('uses an explicit child auth object instead of inheriting the parent auth', () => {
|
||||
const parentAuth = { type: 'bearer', token: 'parent-token' };
|
||||
const childAuth = { type: 'basic', username: 'user', password: 'pass' };
|
||||
const merged = mergeRequestConfig({ auth: parentAuth }, { auth: childAuth });
|
||||
|
||||
assert.deepEqual(merged.auth, childAuth);
|
||||
});
|
||||
|
||||
test('reads auth inherit from a Bruno-style http block', () => {
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'bruno-auth-'));
|
||||
const childDir = path.join(tempRoot, 'nested');
|
||||
fs.mkdirSync(childDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(tempRoot, 'opencollection.yml'), `request:\n auth:\n type: bearer\n token: "parent-token"\n`);
|
||||
fs.writeFileSync(path.join(tempRoot, 'folder.yml'), 'auth: inherit\n');
|
||||
fs.writeFileSync(path.join(childDir, 'request.yml'), 'http:\n auth: inherit\n');
|
||||
|
||||
const config = getRequestConfigForFile(path.join(childDir, 'request.yml'), tempRoot);
|
||||
|
||||
assert.deepEqual(config.auth, { type: 'bearer', token: 'parent-token' });
|
||||
});
|
||||
|
||||
test('renders dotenv placeholders in generated variable definitions', () => {
|
||||
const request = {
|
||||
url: 'https://example.test',
|
||||
params: [
|
||||
{ name: 'username', value: '{{elixFormsApiUsername}}', type: 'path' },
|
||||
],
|
||||
};
|
||||
|
||||
const output = buildRequestContent(request, 'Logout', {}, new Set(['elixFormsApiUsername']));
|
||||
|
||||
assert.match(output, /@username = "\{\{\$dotenv elixFormsApiUsername\}\}"/);
|
||||
});
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "generate-http-requests",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "generate-http-requests",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"strip-json-comments": "^5.0.3",
|
||||
"yaml": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
|
||||
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
||||
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "generate-http-requests",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"strip-json-comments": "^5.0.3",
|
||||
"yaml": "^2.9.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user