diff --git a/package.json b/package.json index f328450..ed0d52c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "devDependencies": { "@types/mocha": "^5.2.7", "@types/node": "^12.12.14", - "@types/uuid": "^3.4.6", "mocha": "^6.2.2", "nyc": "^14.1.1", "prettier": "^1.19.1", @@ -45,7 +44,6 @@ "dependencies": { "commander": "^4.0.1", "cucumber-messages": "7.0.0", - "source-map-support": "^0.5.16", - "uuid": "^3.3.3" + "source-map-support": "^0.5.16" } } diff --git a/src/AstBuilder.ts b/src/AstBuilder.ts index 3a2d01b..fe38f60 100644 --- a/src/AstBuilder.ts +++ b/src/AstBuilder.ts @@ -1,17 +1,16 @@ import AstNode from './AstNode' -import { messages } from 'cucumber-messages' +import { messages, IdGenerator } from 'cucumber-messages' import { RuleType, TokenType } from './Parser' import Token from './Token' import { AstBuilderException } from './Errors' -import { NewId } from './types' import createLocation from './cli/createLocation' export default class AstBuilder { private stack: AstNode[] private comments: messages.GherkinDocument.IComment[] - private readonly newId: NewId + private readonly newId: IdGenerator.NewId - constructor(newId: NewId) { + constructor(newId: IdGenerator.NewId) { this.newId = newId if (!newId) { throw new Error('No newId') diff --git a/src/Gherkin.ts b/src/Gherkin.ts new file mode 100644 index 0000000..6316b2b --- /dev/null +++ b/src/Gherkin.ts @@ -0,0 +1,97 @@ +import { PassThrough, pipeline, Readable } from 'stream' +import { BinaryToMessageStream, messages } from 'cucumber-messages' +import ParserMessageStream from './stream/ParserMessageStream' +import GherkinExe from './external/GherkinExe' +import fs from 'fs' +import SourceMessageStream from './stream/SourceMessageStream' +import Dialect from './Dialect' +import DIALECTS from './gherkin-languages.json' +import IGherkinOptions from './IGherkinOptions' +import makeGherkinOptions from './makeGherkinOptions' + +function fromStream(stream: Readable, options: IGherkinOptions = {}) { + return pipeline( + stream, + new BinaryToMessageStream(messages.Envelope.decodeDelimited), + new ParserMessageStream(options) + ) +} + +function fromPaths(paths: string[], options: IGherkinOptions = {}): Readable { + options = makeGherkinOptions(options) + + if (process.env.GHERKIN_EXECUTABLE) { + return new GherkinExe( + process.env.GHERKIN_EXECUTABLE, + paths, + [], + options + ).messageStream() + } + + const combinedMessageStream = new PassThrough({ + writableObjectMode: true, + readableObjectMode: true, + }) + + function pipeSequentially() { + const path = paths.shift() + if (path !== undefined) { + const parserMessageStream = new ParserMessageStream(options) + parserMessageStream.on('end', () => { + pipeSequentially() + }) + + const end = paths.length === 0 + fs.createReadStream(path, { encoding: 'utf-8' }) + .pipe(new SourceMessageStream(path)) + .pipe(parserMessageStream) + .pipe(combinedMessageStream, { end }) + } + } + pipeSequentially() + return combinedMessageStream +} + +function fromSources( + envelopes: messages.IEnvelope[], + options: IGherkinOptions = {} +): Readable { + options = makeGherkinOptions(options) + if (process.env.GHERKIN_EXECUTABLE) { + return new GherkinExe( + process.env.GHERKIN_EXECUTABLE, + [], + envelopes, + options + ).messageStream() + } + + const combinedMessageStream = new PassThrough({ objectMode: true }) + + function pipeSequentially() { + const envelope = envelopes.shift() + if (envelope !== undefined && envelope.source) { + const parserMessageStream = new ParserMessageStream(options) + parserMessageStream.pipe(combinedMessageStream, { + end: envelopes.length === 0, + }) + parserMessageStream.on('end', pipeSequentially) + parserMessageStream.end(envelope) + } + } + pipeSequentially() + + return combinedMessageStream +} + +function dialects(): { [key: string]: Dialect } { + return DIALECTS +} + +export default { + fromPaths, + fromStream, + fromSources, + dialects, +} diff --git a/src/IGherkinOptions.ts b/src/IGherkinOptions.ts new file mode 100644 index 0000000..5fc120a --- /dev/null +++ b/src/IGherkinOptions.ts @@ -0,0 +1,9 @@ +import { IdGenerator } from 'cucumber-messages' + +export default interface IGherkinOptions { + defaultDialect?: string + includeSource?: boolean + includeGherkinDocument?: boolean + includePickles?: boolean + newId?: IdGenerator.NewId +} diff --git a/src/IdGenerator.ts b/src/IdGenerator.ts deleted file mode 100644 index 8f9db5f..0000000 --- a/src/IdGenerator.ts +++ /dev/null @@ -1,11 +0,0 @@ -import uuidv4 from 'uuid/v4' -import { NewId } from './types' - -export function uuid(): NewId { - return () => uuidv4() -} - -export function incrementing(): NewId { - let next = 0 - return () => (next++).toString() -} diff --git a/src/cli/main.ts b/src/cli/main.ts index a52fdb9..7f64959 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -1,10 +1,13 @@ import { Command } from 'commander' import packageJson from '../../package.json' -import gherkin from '../index' -import { IGherkinOptions } from '../types' -import { MessageToBinaryStream, MessageToNdjsonStream } from 'cucumber-messages' +import Gherkin from '../Gherkin' +import { + MessageToBinaryStream, + MessageToNdjsonStream, + IdGenerator, +} from 'cucumber-messages' import { Readable, Transform } from 'stream' -import { incrementing, uuid } from '../IdGenerator' +import IGherkinOptions from '../IGherkinOptions' const program = new Command() program.version(packageJson.version) @@ -25,13 +28,15 @@ const options: IGherkinOptions = { includeSource: program.source, includeGherkinDocument: program.ast, includePickles: program.pickles, - newId: program.predictableIds ? incrementing() : uuid(), + newId: program.predictableIds + ? IdGenerator.incrementing() + : IdGenerator.uuid(), } const messageStream = paths.length === 0 - ? gherkin.fromStream((process.stdin as unknown) as Readable, options) - : gherkin.fromPaths(paths, options) + ? Gherkin.fromStream((process.stdin as unknown) as Readable, options) + : Gherkin.fromPaths(paths, options) let encodedStream: Transform switch (program.format) { diff --git a/src/external/GherkinExe.ts b/src/external/GherkinExe.ts index 85d5d82..159c15a 100644 --- a/src/external/GherkinExe.ts +++ b/src/external/GherkinExe.ts @@ -2,7 +2,7 @@ import { spawn, spawnSync } from 'child_process' import { messages, BinaryToMessageStream } from 'cucumber-messages' import { Readable } from 'stream' import Dialect from '../Dialect' -import { IGherkinOptions } from '../types' +import IGherkinOptions from '../IGherkinOptions' export default class GherkinExe { constructor( diff --git a/src/index.ts b/src/index.ts index 5dcdbb4..f7c3f9c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,102 +1,5 @@ -import { gherkinOptions, IGherkinOptions } from './types' -import { PassThrough, Readable, pipeline } from 'stream' -import ParserMessageStream from './stream/ParserMessageStream' -import fs from 'fs' -import SourceMessageStream from './stream/SourceMessageStream' -import { messages, BinaryToMessageStream } from 'cucumber-messages' -import DIALECTS from './gherkin-languages.json' -import Dialect from './Dialect' -import GherkinExe from './external/GherkinExe' -import { uuid, incrementing } from './IdGenerator' +import Gherkin from './Gherkin' +import IGherkinOptions from './IGherkinOptions' -export function fromStream(stream: Readable, options: IGherkinOptions = {}) { - return pipeline( - stream, - new BinaryToMessageStream(messages.Envelope.decodeDelimited), - new ParserMessageStream(options) - ) -} - -export function fromPaths( - paths: string[], - options: IGherkinOptions = {} -): Readable { - options = gherkinOptions(options) - - if (process.env.GHERKIN_EXECUTABLE) { - return new GherkinExe( - process.env.GHERKIN_EXECUTABLE, - paths, - [], - options - ).messageStream() - } - - const combinedMessageStream = new PassThrough({ - writableObjectMode: true, - readableObjectMode: true, - }) - - function pipeSequentially() { - const path = paths.shift() - if (path !== undefined) { - const parserMessageStream = new ParserMessageStream(options) - parserMessageStream.on('end', () => { - pipeSequentially() - }) - - const end = paths.length === 0 - fs.createReadStream(path, { encoding: 'utf-8' }) - .pipe(new SourceMessageStream(path)) - .pipe(parserMessageStream) - .pipe(combinedMessageStream, { end }) - } - } - pipeSequentially() - return combinedMessageStream -} - -export function fromSources( - envelopes: messages.IEnvelope[], - options: IGherkinOptions = {} -): Readable { - options = gherkinOptions(options) - if (process.env.GHERKIN_EXECUTABLE) { - return new GherkinExe( - process.env.GHERKIN_EXECUTABLE, - [], - envelopes, - options - ).messageStream() - } - - const combinedMessageStream = new PassThrough({ objectMode: true }) - - function pipeSequentially() { - const envelope = envelopes.shift() - if (envelope !== undefined && envelope.source) { - const parserMessageStream = new ParserMessageStream(options) - parserMessageStream.pipe(combinedMessageStream, { - end: envelopes.length === 0, - }) - parserMessageStream.on('end', pipeSequentially) - parserMessageStream.end(envelope) - } - } - pipeSequentially() - - return combinedMessageStream -} - -export function dialects(): { [key: string]: Dialect } { - return DIALECTS -} - -export default { - fromStream, - fromPaths, - fromSources, - dialects, - uuid, - incrementing, -} +export default Gherkin +export { IGherkinOptions } diff --git a/src/makeGherkinOptions.ts b/src/makeGherkinOptions.ts new file mode 100644 index 0000000..9d7a7c5 --- /dev/null +++ b/src/makeGherkinOptions.ts @@ -0,0 +1,14 @@ +import { IdGenerator } from 'cucumber-messages' +import IGherkinOptions from './IGherkinOptions' + +const defaultOptions: IGherkinOptions = { + defaultDialect: 'en', + includeSource: true, + includeGherkinDocument: true, + includePickles: true, + newId: IdGenerator.uuid(), +} + +export default function gherkinOptions(options: IGherkinOptions) { + return { ...defaultOptions, ...options } +} diff --git a/src/pickles/compile.ts b/src/pickles/compile.ts index f42e559..3c0a2d5 100644 --- a/src/pickles/compile.ts +++ b/src/pickles/compile.ts @@ -1,11 +1,10 @@ -import { messages } from 'cucumber-messages' +import { messages, IdGenerator } from 'cucumber-messages' import IGherkinDocument = messages.IGherkinDocument -import { NewId } from '../types' export default function compile( gherkinDocument: IGherkinDocument, uri: string, - newId: NewId + newId: IdGenerator.NewId ) { const pickles: messages.IPickle[] = [] @@ -63,7 +62,7 @@ function compileRule( language: string, pickles: messages.IPickle[], uri: string, - newId: NewId + newId: IdGenerator.NewId ) { let ruleBackgroundSteps = [].concat(featureBackgroundSteps) @@ -103,7 +102,7 @@ function compileScenario( language: string, pickles: messages.IPickle[], uri: string, - newId: NewId + newId: IdGenerator.NewId ) { const steps = scenario.steps.length === 0 @@ -133,7 +132,7 @@ function compileScenarioOutline( language: string, pickles: messages.IPickle[], uri: string, - newId: NewId + newId: IdGenerator.NewId ) { scenario.examples .filter(e => e.tableHeader !== null) @@ -234,7 +233,7 @@ function pickleSteps( scenario: messages.GherkinDocument.Feature.IScenario, variableCells: messages.GherkinDocument.Feature.TableRow.ITableCell[], valuesRow: messages.GherkinDocument.Feature.ITableRow, - newId: NewId + newId: IdGenerator.NewId ) { return scenario.steps.map(s => pickleStep(s, variableCells, valuesRow, newId)) } @@ -243,7 +242,7 @@ function pickleStep( step: messages.GherkinDocument.Feature.IStep, variableCells: messages.GherkinDocument.Feature.TableRow.ITableCell[], valuesRow: messages.GherkinDocument.Feature.ITableRow | null, - newId: NewId + newId: IdGenerator.NewId ) { const astNodeIds = [step.id] if (valuesRow) { diff --git a/src/stream/ParserMessageStream.ts b/src/stream/ParserMessageStream.ts index dff2946..1c639c2 100644 --- a/src/stream/ParserMessageStream.ts +++ b/src/stream/ParserMessageStream.ts @@ -1,7 +1,7 @@ import generateMessages from './generateMessages' import { Transform, TransformCallback } from 'stream' -import { IGherkinOptions } from '../types' import { messages } from 'cucumber-messages' +import IGherkinOptions from '../IGherkinOptions' /** * Stream that reads Source messages and writes GherkinDocument and Pickle messages. diff --git a/src/stream/generateMessages.ts b/src/stream/generateMessages.ts index fcdc39d..fef9996 100644 --- a/src/stream/generateMessages.ts +++ b/src/stream/generateMessages.ts @@ -1,9 +1,9 @@ import Parser from '../Parser' import TokenMatcher from '../TokenMatcher' import { messages } from 'cucumber-messages' -import { IGherkinOptions } from '../types' import compile from '../pickles/compile' import AstBuilder from '../AstBuilder' +import IGherkinOptions from '../IGherkinOptions' export default function generateMessages( data: string, diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index c915dbc..0000000 --- a/src/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { uuid } from './IdGenerator' - -export interface IGherkinOptions { - defaultDialect?: string - includeSource?: boolean - includeGherkinDocument?: boolean - includePickles?: boolean - newId?: NewId -} - -const defaultOptions: IGherkinOptions = { - defaultDialect: 'en', - includeSource: true, - includeGherkinDocument: true, - includePickles: true, - newId: uuid(), -} - -export function gherkinOptions(options: IGherkinOptions) { - return { ...defaultOptions, ...options } -} - -export type NewId = () => string diff --git a/test/ParserTest.ts b/test/ParserTest.ts index 9e38103..4971e02 100644 --- a/test/ParserTest.ts +++ b/test/ParserTest.ts @@ -1,14 +1,13 @@ -import * as assert from 'assert' -import { messages } from 'cucumber-messages' +import assert from 'assert' +import { messages, IdGenerator } from 'cucumber-messages' import AstBuilder from '../src/AstBuilder' import Parser from '../src/Parser' import TokenScanner from '../src/TokenScanner' import TokenMatcher from '../src/TokenMatcher' -import { incrementing } from '../src/IdGenerator' describe('Parser', function() { it('parses a simple feature', function() { - const parser = new Parser(new AstBuilder(incrementing())) + const parser = new Parser(new AstBuilder(IdGenerator.incrementing())) const scanner = new TokenScanner('Feature: hello') const matcher = new TokenMatcher() const ast = parser.parse(scanner, matcher) @@ -29,7 +28,7 @@ describe('Parser', function() { }) it('parses multiple features', function() { - const parser = new Parser(new AstBuilder(incrementing())) + const parser = new Parser(new AstBuilder(IdGenerator.incrementing())) const matcher = new TokenMatcher() const ast1 = parser.parse(new TokenScanner('Feature: hello'), matcher) const ast2 = parser.parse(new TokenScanner('Feature: hello again'), matcher) @@ -65,7 +64,7 @@ describe('Parser', function() { }) it('parses feature after parse error', function() { - const parser = new Parser(new AstBuilder(incrementing())) + const parser = new Parser(new AstBuilder(IdGenerator.incrementing())) const matcher = new TokenMatcher() let ast: messages.IGherkinDocument try { @@ -135,7 +134,7 @@ describe('Parser', function() { }) it('can change the default language', function() { - const parser = new Parser(new AstBuilder(incrementing())) + const parser = new Parser(new AstBuilder(IdGenerator.incrementing())) const matcher = new TokenMatcher('no') const scanner = new TokenScanner('Egenskap: i18n support') const ast = parser.parse(scanner, matcher) diff --git a/test/StreamTest.ts b/test/StreamTest.ts index 5de5ee2..2a5d03d 100644 --- a/test/StreamTest.ts +++ b/test/StreamTest.ts @@ -1,13 +1,13 @@ import assert from 'assert' import { Readable } from 'stream' import { messages } from 'cucumber-messages' -import { fromPaths, fromSources, dialects } from '../src' +import Gherkin from '../src/Gherkin' import makeSourceEnvelope from '../src/stream/makeSourceEnvelope' describe('gherkin', () => { it('parses gherkin from the file system', async () => { const envelopes = await streamToArray( - fromPaths(['testdata/good/minimal.feature']) + Gherkin.fromPaths(['testdata/good/minimal.feature']) ) assert.strictEqual(envelopes.length, 3) }) @@ -22,7 +22,7 @@ describe('gherkin', () => { 'test.feature' ) - const envelopes = await streamToArray(fromSources([source])) + const envelopes = await streamToArray(Gherkin.fromSources([source])) assert.strictEqual(envelopes.length, 3) }) @@ -35,14 +35,14 @@ describe('gherkin', () => { 'test.feature' ) const envelopes = await streamToArray( - fromSources([source], { defaultDialect: 'fr' }) + Gherkin.fromSources([source], { defaultDialect: 'fr' }) ) assert.strictEqual(envelopes.length, 3) }) it('outputs dialects', async () => { - const result = dialects() - assert.strictEqual(result.en.name, 'English') + const dialects = Gherkin.dialects() + assert.strictEqual(dialects.en.name, 'English') }) })