Skip to content

Commit d1bf16f

Browse files
aloisklinkMindaugasLaganeckas
authored andcommitted
fix: improve stdin input handling
Improve how mmdc handles stdin inputs. Currently, running `mmdc` exits with an error if it cannot find an stdin. However, it does this by checking if the stdin is a FIFO, which is only true in some cases. (it seems to fail on Windows and other non-bash shells). I've changed `mmdc` to always read from stdin if there is no `--input`. However, it prints a warning. To suppress this warning, you can run mmdc with `--input -`.
1 parent c851234 commit d1bf16f

File tree

3 files changed

+31
-13
lines changed

3 files changed

+31
-13
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ You can easily pipe input from stdin. This example shows how to use a heredoc to
109109
send a diagram as stdin to mermaid-cli (mmdc).
110110

111111
```sh
112-
cat << EOF | mmdc
112+
cat << EOF | mmdc --input -
113113
graph TD
114114
A[Client] --> B[Load Balancer]
115115
EOF

src-test/test.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'fs/promises'
22
// Can't use async to load workflow entries, see https://github.com/facebook/jest/issues/2235
3-
import { readdirSync } from 'fs'
3+
import { readdirSync, createReadStream } from 'fs'
44
import { exec, execFile } from 'child_process'
55

66
// Joins together directory/file names in a OS independent way
@@ -12,6 +12,7 @@ import { expect, beforeAll, afterAll, describe, test } from '@jest/globals'
1212

1313
import { run, renderMermaid, parseMMD } from '../src/index.js'
1414
import puppeteer from 'puppeteer'
15+
import { pipeline } from 'stream'
1516

1617
const workflows = ['test-positive', 'test-negative']
1718
const out = 'test-output'
@@ -154,8 +155,24 @@ describe('mermaid-cli', () => {
154155
).rejects.toThrow('TimeoutError: Timed out after 1 ms')
155156
}, timeout)
156157

157-
test('should error on missing input', async () => {
158-
await expect(promisify(execFile)('node', ['src/cli.js'])).rejects.toThrow()
158+
test('should warn when reading from stdin with missing --input', async () => {
159+
const execFilePromise = promisify(execFile)('node', ['src/cli.js'])
160+
await promisify(pipeline)(
161+
createReadStream('test-positive/flowchart1.mmd'),
162+
execFilePromise.child.stdin
163+
)
164+
const { stderr } = await execFilePromise
165+
expect(stderr).toContain('No input file specfied, reading from stdin.')
166+
}, timeout)
167+
168+
test('should not warn when reading from stdin with `--input -`', async () => {
169+
const execFilePromise = promisify(execFile)('node', ['src/cli.js', '--input', '-'])
170+
await promisify(pipeline)(
171+
createReadStream('test-positive/flowchart1.mmd'),
172+
execFilePromise.child.stdin
173+
)
174+
const { stderr } = await execFilePromise
175+
expect(stderr).not.toContain('No input file specfied, reading from stdin.')
159176
}, timeout)
160177

161178
test('should error on mermaid syntax error', async () => {

src/index.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ const checkConfigFile = file => {
2727
}
2828
}
2929

30-
const inputPipedFromStdin = () => fs.fstatSync(0).isFIFO()
31-
3230
const getInputData = async inputFile => new Promise((resolve, reject) => {
3331
// if an input file has been specified using '-i', it takes precedence over
3432
// piping from stdin
@@ -84,7 +82,7 @@ async function cli () {
8482
.addOption(new Option('-t, --theme [theme]', 'Theme of the chart').choices(['default', 'forest', 'dark', 'neutral']).default('default'))
8583
.addOption(new Option('-w, --width [width]', 'Width of the page').argParser(parseCommanderInt).default(800))
8684
.addOption(new Option('-H, --height [height]', 'Height of the page').argParser(parseCommanderInt).default(600))
87-
.option('-i, --input <input>', 'Input mermaid file. Files ending in .md will be treated as Markdown and all charts (e.g. ```mermaid (...)```) will be extracted and generated. Required.')
85+
.option('-i, --input <input>', 'Input mermaid file. Files ending in .md will be treated as Markdown and all charts (e.g. ```mermaid (...)```) will be extracted and generated. Use `-` to read from stdin.')
8886
.option('-o, --output [output]', 'Output file. It should be either md, svg, png or pdf. Optional. Default: input + ".svg"')
8987
.addOption(new Option('-e, --outputFormat [format]', 'Output format for the generated image.').choices(['svg', 'png', 'pdf']).default(null, 'Loaded from the output file extension'))
9088
.addOption(new Option('-b, --backgroundColor [backgroundColor]', 'Background color for pngs/svgs (not pdfs). Example: transparent, red, \'#F0F0F0\'.').default('white'))
@@ -101,12 +99,15 @@ async function cli () {
10199
let { theme, width, height, input, output, outputFormat, backgroundColor, configFile, cssFile, puppeteerConfigFile, scale, pdfFit, quiet } = options
102100

103101
// check input file
104-
if (!(input || inputPipedFromStdin())) {
105-
console.error(chalk.red('\nPlease specify input file: -i <input>\n'))
106-
// Log to stderr, and return with error exitCode
107-
commander.help({ error: true })
108-
}
109-
if (input && !fs.existsSync(input)) {
102+
if (!input) {
103+
warn('No input file specfied, reading from stdin. ' +
104+
'If you want to specify an input file, please use `-i <input>.` ' +
105+
'You can use `-i -` to read from stdin and to suppress this warning.'
106+
)
107+
} else if (input === '-') {
108+
// `--input -` means read from stdin, but suppress the above warning
109+
input = undefined
110+
} else if (!fs.existsSync(input)) {
110111
error(`Input file "${input}" doesn't exist`)
111112
}
112113

0 commit comments

Comments
 (0)