diff --git a/.changeset/big-jeans-press.md b/.changeset/big-jeans-press.md new file mode 100644 index 000000000..72860338e --- /dev/null +++ b/.changeset/big-jeans-press.md @@ -0,0 +1,5 @@ +--- +'@gitbook/react-openapi': patch +--- + +Add heredoc support for cURL JSON body diff --git a/packages/react-openapi/src/code-samples.ts b/packages/react-openapi/src/code-samples.ts index 370204f27..7c357b4fb 100644 --- a/packages/react-openapi/src/code-samples.ts +++ b/packages/react-openapi/src/code-samples.ts @@ -81,8 +81,6 @@ ${headerString}${bodyString}`; label: 'cURL', syntax: 'bash', generate: ({ method, url: { origin, path }, headers, body }) => { - const separator = ' \\\n'; - const lines: string[] = ['curl -L']; if (method.toUpperCase() !== 'GET') { @@ -114,7 +112,7 @@ ${headerString}${bodyString}`; } } - return lines.map((line, index) => (index > 0 ? indent(line, 2) : line)).join(separator); + return buildHeredoc(lines); }, }, { @@ -262,7 +260,15 @@ const BodyGenerators = { } else if (isYAML(contentType)) { body = `--data-binary $'${yaml.dump(body).replace(/'/g, '').replace(/\\n/g, '\n')}'`; } else { - body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n')}'`; + // For JSON, check if it contains single quotes, if so, use heredoc to avoid single quote issues + const jsonString = stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n'); + if (jsonString.includes("'")) { + // Use heredoc format: return array with --data @- <<'EOF', JSON lines, and EOF + const jsonLines = jsonString.split('\n'); + body = ["--data @- <<'EOF'", ...jsonLines, 'EOF']; + } else { + body = `--data '${jsonString}'`; + } } return { @@ -447,3 +453,37 @@ function convertBodyToXML(body: any): string { return json2xml(body).replace(/"/g, '').replace(/\\n/g, '\n').replace(/\\t/g, '\t'); } + +/** + * Builds a heredoc string from an array of lines + */ +function buildHeredoc(lines: string[]): string { + const separator = ' \\\n'; + let result = ''; + let inHeredoc = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (!line) continue; + + const isHeredocStart = line.includes("<<'EOF'"); + const isHeredocEnd = inHeredoc && line === 'EOF'; + + if (isHeredocStart) { + inHeredoc = true; + // Heredoc start: indent with backslash continuation from previous line, then newline + result += `${i > 0 ? indent(line, 2) : line}\n`; + } else if (isHeredocEnd) { + inHeredoc = false; + // EOF: no indent, no backslash, no separator + result += line; + } else if (inHeredoc) { + // Inside heredoc: indent JSON lines, no backslash, newline + result += `${indent(line, 2)}\n`; + } else { + // Normal line: indent and add separator (backslash) if not last line + result += `${i > 0 ? indent(line, 2) : line}${i < lines.length - 1 ? separator : ''}`; + } + } + return result; +}