diff --git a/src/app.ts b/src/app.ts index 91f2378..6a2773a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,7 +5,7 @@ import { loadConfig } from './config'; import { CONFIG_ARGS_NAME, CONFIG_ARGS_PATH, MODE, parseArgs, VALID_MODES } from './config/args'; import { YamlParser } from './parser/YamlParser'; import { createRuleSelector, createRuleSources, loadRules, resolveRules, visitRules } from './rule'; -import { loadSource, writeSource } from './source'; +import { readSource, writeSource } from './source'; import { VERSION_INFO } from './version'; import { VisitorContext } from './visitor/VisitorContext'; @@ -60,7 +60,7 @@ export async function main(argv: Array): Promise { } const parser = new YamlParser(); - const source = await loadSource(args.source); + const source = await readSource(args.source); const docs = parser.parse(source); for (const data of docs) { diff --git a/src/source.ts b/src/source.ts index 2e47a76..5538391 100644 --- a/src/source.ts +++ b/src/source.ts @@ -8,11 +8,9 @@ export const readDir = promisify(readdir); export const readFile = promisify(readBack); export const writeFile = promisify(writeBack); -export async function loadSource(path: string): Promise { +export async function readSource(path: string, stream = process.stdin): Promise { if (path === '-') { - return readFile(0, { - encoding: FILE_ENCODING, - }); + return readStream(stream, FILE_ENCODING); } else { return readFile(path, { encoding: FILE_ENCODING, @@ -20,20 +18,36 @@ export async function loadSource(path: string): Promise { } } -export async function writeSource(path: string, data: string): Promise { - if (path === '-') { - return new Promise((res, rej) => { - process.stdout.write(data, (err: Error | null | undefined) => { - if (isNil(err)) { - res(); - } else { - rej(err); - } - }); +export function readStream(stream: NodeJS.ReadStream, encoding: string): Promise { + return new Promise((res, rej) => { + const chunks: Array = []; + stream.on('data', (chunk) => chunks.push(chunk)); + stream.on('end', () => { + const result = Buffer.concat(chunks).toString(encoding); + res(result); }); + stream.on('error', (err) => rej(err)); + }); +} + +export async function writeSource(path: string, data: string, stream = process.stdout): Promise { + if (path === '-') { + return writeStream(stream, data); } else { return writeFile(path, data, { encoding: FILE_ENCODING, }); } } + +export function writeStream(stream: NodeJS.WriteStream, data: string): Promise { + return new Promise((res, rej) => { + stream.write(data, (err: Error | null | undefined) => { + if (isNil(err)) { + res(); + } else { + rej(err); + } + }); + }); +} diff --git a/test/TestSource.ts b/test/TestSource.ts new file mode 100644 index 0000000..fde8394 --- /dev/null +++ b/test/TestSource.ts @@ -0,0 +1,59 @@ +import { expect } from 'chai'; +import mockFs from 'mock-fs'; +import { spy } from 'sinon'; +import { PassThrough } from 'stream'; + +import { readSource, writeSource } from '../src/source'; +import { describeLeaks, itLeaks } from './helpers/async'; + +export const TEST_STRING = 'hello world'; + +describeLeaks('load source helper', async () => { + itLeaks('should read from stdin', async () => { + const pt = new PassThrough(); + + const futureSource = readSource('-', pt); + pt.emit('data', Buffer.from(TEST_STRING)); + pt.end(); + pt.destroy(); + + const source = await futureSource; + expect(source).to.equal(TEST_STRING); + }); + + it('should read from a file', async () => { + mockFs({ + test: TEST_STRING, + }); + + const source = await readSource('test'); + mockFs.restore(); + + expect(source).to.equal(TEST_STRING); + }); +}); + +describeLeaks('write source helper', async () => { + it('should write to a file', async () => { + mockFs({ + test: 'empty', + }); + + await writeSource('test', TEST_STRING); + const source = await readSource('test'); + mockFs.restore(); + + expect(source).to.equal(TEST_STRING); + }); + + it('should write to stdout', async () => { + const pt = new PassThrough(); + const writeSpy = spy(pt, 'write'); + + await writeSource('-', TEST_STRING, pt); + pt.end(); + pt.destroy(); + + expect(writeSpy).to.have.been.calledWith(TEST_STRING); + }); +});