prettier-plugin-gherkin
Advanced tools
Comparing version 1.1.1 to 2.0.0
# CHANGELOG | ||
## 1.1.1 | ||
## [2.0.0] - 2023-08-31 | ||
### Changed | ||
- [Breaking] Handle prettier 3. Prettier 2 is not compatible with this version as the plugin API did change totally. | ||
- [Breaking] (maybe) drop support for node 14: prettier 3 does still support node 14, but it is unmaintained, and [the tests does not pass on node 14 only](https://github.com/mapado/prettier-plugin-gherkin/actions/runs/6030258360/job/16361678638?pr=9). If you are willing to spend some time on this, feel free to open a PR, but I suggest you to migrate to a more recent version of node. [#9](https://github.com/mapado/prettier-plugin-gherkin/pull/9) by [@jdeniau](https://github.com/jdeniau) | ||
## [1.1.1] | ||
### Fixes | ||
@@ -9,3 +16,3 @@ | ||
## 1.1.0 | ||
## [1.1.0] | ||
@@ -16,4 +23,4 @@ - handle "language" comment | ||
## 1.0.0 | ||
## [1.0.0] | ||
Version 1.0.0 is a complete rewrite of the [first version](https://github.com/armandabric/prettier-plugin-gherkin) by [@armandabric](https://github.com/armandabric). |
@@ -1,8 +0,4 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.TypedDataTable = exports.TypedDocString = exports.TypedTableCell = exports.TypedTableRow = exports.TypedExamples = exports.TypedScenario = exports.TypedStep = exports.TypedBackground = exports.TypedRuleChild = exports.TypedRule = exports.TypedFeatureChild = exports.TypedComment = exports.TypedTag = exports.TypedFeature = exports.TypedGherkinDocument = exports.TypedGherkinNodeWithLocation = exports.isHasChild = exports.isHasChildren = exports.TypedGherkinNode = exports.isWithLocation = void 0; | ||
function isWithLocation(node) { | ||
export function isWithLocation(node) { | ||
return typeof node === 'object' && node !== null && 'location' in node; | ||
} | ||
exports.isWithLocation = isWithLocation; | ||
function reEscapeTableCell(str) { | ||
@@ -34,15 +30,13 @@ const out = str | ||
} | ||
class TypedGherkinNode { | ||
export class TypedGherkinNode { | ||
constructor(originalNode) { } | ||
} | ||
exports.TypedGherkinNode = TypedGherkinNode; | ||
function isHasChildren(node) { | ||
export function isHasChildren(node) { | ||
return typeof node === 'object' && node !== null && 'children' in node; | ||
} | ||
exports.isHasChildren = isHasChildren; | ||
function isHasChild(node) { | ||
export function isHasChild(node) { | ||
return typeof node === 'object' && node !== null && 'child' in node; | ||
} | ||
exports.isHasChild = isHasChild; | ||
class TypedGherkinNodeWithLocation extends TypedGherkinNode { | ||
export class TypedGherkinNodeWithLocation extends TypedGherkinNode { | ||
location; | ||
constructor(originalNode) { | ||
@@ -53,3 +47,2 @@ super(originalNode); | ||
} | ||
exports.TypedGherkinNodeWithLocation = TypedGherkinNodeWithLocation; | ||
/** | ||
@@ -61,3 +54,6 @@ * An options object that will be set for a document | ||
let OPTIONS; | ||
class TypedGherkinDocument extends TypedGherkinNode { | ||
export class TypedGherkinDocument extends TypedGherkinNode { | ||
uri; | ||
feature; | ||
comments; | ||
constructor(originalNode, options) { | ||
@@ -76,4 +72,9 @@ super(originalNode); | ||
} | ||
exports.TypedGherkinDocument = TypedGherkinDocument; | ||
class TypedFeature extends TypedGherkinNodeWithLocation { | ||
export class TypedFeature extends TypedGherkinNodeWithLocation { | ||
tags; | ||
language; | ||
keyword; | ||
name; | ||
description; | ||
children; | ||
constructor(originalNode) { | ||
@@ -89,4 +90,5 @@ super(originalNode); | ||
} | ||
exports.TypedFeature = TypedFeature; | ||
class TypedTag extends TypedGherkinNodeWithLocation { | ||
export class TypedTag extends TypedGherkinNodeWithLocation { | ||
name; | ||
id; | ||
constructor(originalNode) { | ||
@@ -98,4 +100,4 @@ super(originalNode); | ||
} | ||
exports.TypedTag = TypedTag; | ||
class TypedComment extends TypedGherkinNodeWithLocation { | ||
export class TypedComment extends TypedGherkinNodeWithLocation { | ||
text; | ||
constructor(originalNode) { | ||
@@ -109,4 +111,6 @@ super(originalNode); | ||
} | ||
exports.TypedComment = TypedComment; | ||
class TypedFeatureChild extends TypedGherkinNode { | ||
export class TypedFeatureChild extends TypedGherkinNode { | ||
rule; | ||
background; | ||
scenario; | ||
constructor(originalNode) { | ||
@@ -128,4 +132,9 @@ super(originalNode); | ||
} | ||
exports.TypedFeatureChild = TypedFeatureChild; | ||
class TypedRule extends TypedGherkinNodeWithLocation { | ||
export class TypedRule extends TypedGherkinNodeWithLocation { | ||
tags; | ||
keyword; | ||
name; | ||
description; | ||
children; | ||
id; | ||
constructor(originalNode) { | ||
@@ -141,4 +150,5 @@ super(originalNode); | ||
} | ||
exports.TypedRule = TypedRule; | ||
class TypedRuleChild extends TypedGherkinNode { | ||
export class TypedRuleChild extends TypedGherkinNode { | ||
background; | ||
scenario; | ||
constructor(originalNode) { | ||
@@ -157,4 +167,8 @@ super(originalNode); | ||
} | ||
exports.TypedRuleChild = TypedRuleChild; | ||
class TypedBackground extends TypedGherkinNodeWithLocation { | ||
export class TypedBackground extends TypedGherkinNodeWithLocation { | ||
keyword; | ||
name; | ||
description; | ||
steps; | ||
id; | ||
constructor(originalNode) { | ||
@@ -172,4 +186,9 @@ super(originalNode); | ||
} | ||
exports.TypedBackground = TypedBackground; | ||
class TypedStep extends TypedGherkinNodeWithLocation { | ||
export class TypedStep extends TypedGherkinNodeWithLocation { | ||
keyword; | ||
keywordType; | ||
text; | ||
docString; | ||
dataTable; | ||
id; | ||
constructor(originalNode) { | ||
@@ -192,4 +211,10 @@ super(originalNode); | ||
} | ||
exports.TypedStep = TypedStep; | ||
class TypedScenario extends TypedGherkinNodeWithLocation { | ||
export class TypedScenario extends TypedGherkinNodeWithLocation { | ||
tags; | ||
keyword; | ||
name; | ||
description; | ||
steps; | ||
examples; | ||
id; | ||
constructor(originalNode) { | ||
@@ -210,10 +235,15 @@ super(originalNode); | ||
} | ||
exports.TypedScenario = TypedScenario; | ||
class TypedExamples extends TypedGherkinNodeWithLocation { | ||
export class TypedExamples extends TypedGherkinNodeWithLocation { | ||
tags; | ||
keyword; | ||
name; | ||
description; | ||
tableHeader; | ||
tableBody; | ||
id; | ||
constructor(originalNode) { | ||
var _a; | ||
super(originalNode); | ||
const rows = [ | ||
originalNode.tableHeader, | ||
...((_a = originalNode.tableBody) !== null && _a !== void 0 ? _a : []), | ||
...(originalNode.tableBody ?? []), | ||
].filter((row) => typeof row !== 'undefined'); | ||
@@ -239,4 +269,6 @@ const columnSizes = generateColumnSizes(rows); | ||
} | ||
exports.TypedExamples = TypedExamples; | ||
class TypedTableRow extends TypedGherkinNodeWithLocation { | ||
export class TypedTableRow extends TypedGherkinNodeWithLocation { | ||
cells; | ||
id; | ||
columnSizes; | ||
constructor(originalNode, columnSizes) { | ||
@@ -252,12 +284,15 @@ super(originalNode); | ||
} | ||
exports.TypedTableRow = TypedTableRow; | ||
class TypedTableCell extends TypedGherkinNodeWithLocation { | ||
export class TypedTableCell extends TypedGherkinNodeWithLocation { | ||
value; | ||
displaySize; | ||
constructor(originalNode, displaySize) { | ||
super(originalNode); | ||
this.value = reEscapeTableCell(originalNode.value); | ||
this.displaySize = Math.max(displaySize !== null && displaySize !== void 0 ? displaySize : 0, this.value.length); | ||
this.displaySize = Math.max(displaySize ?? 0, this.value.length); | ||
} | ||
} | ||
exports.TypedTableCell = TypedTableCell; | ||
class TypedDocString extends TypedGherkinNodeWithLocation { | ||
export class TypedDocString extends TypedGherkinNodeWithLocation { | ||
mediaType; | ||
content; | ||
delimiter; | ||
constructor(originalNode) { | ||
@@ -270,4 +305,4 @@ super(originalNode); | ||
} | ||
exports.TypedDocString = TypedDocString; | ||
class TypedDataTable extends TypedGherkinNodeWithLocation { | ||
export class TypedDataTable extends TypedGherkinNodeWithLocation { | ||
rows; | ||
constructor(originalNode) { | ||
@@ -282,2 +317,1 @@ super(originalNode); | ||
} | ||
exports.TypedDataTable = TypedDataTable; |
@@ -1,12 +0,9 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const gherkin_1 = require("@cucumber/gherkin"); | ||
const messages_1 = require("@cucumber/messages"); | ||
const prettier_1 = require("prettier"); | ||
const GherkinAST_1 = require("./GherkinAST"); | ||
import { Parser as GherkinParser, AstBuilder, GherkinClassicTokenMatcher, } from '@cucumber/gherkin'; | ||
import { IdGenerator, StepKeywordType, } from '@cucumber/messages'; | ||
import { util, doc, } from 'prettier'; | ||
import { TypedGherkinDocument, TypedComment, TypedFeature, TypedTag, TypedFeatureChild, TypedScenario, TypedStep, TypedDocString, TypedBackground, TypedExamples, TypedTableRow, TypedTableCell, TypedDataTable, TypedRule, TypedRuleChild, isHasChildren, isHasChild, isWithLocation, } from './GherkinAST/index.js'; | ||
const DEFAULT_ESCAPE_BACKSLASH = false; | ||
// taken from https://github.com/cucumber/gherkin-javascript/blob/e25a1be3b21133c7a92eb7735997c6e774406226/src/GherkinClassicTokenMatcher.ts#L11 | ||
const LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/; | ||
const { literalline, hardline, join, group, trim, indent, line } = prettier_1.doc.builders; | ||
const { hasNewline, isPreviousLineEmpty, makeString } = prettier_1.util; | ||
const { hardline, join, indent } = doc.builders; | ||
const languages = [ | ||
@@ -44,3 +41,3 @@ { | ||
let currentLine = 1; | ||
while ((index = prettier_1.util.skipEverythingButNewLine(text, index)) !== false) { | ||
while ((index = util.skipEverythingButNewLine(text, index)) !== false) { | ||
columnSizes.push(index + 1); | ||
@@ -54,8 +51,7 @@ index++; | ||
const gherkinParser = { | ||
parse: (text, parsers, options) => { | ||
var _a; | ||
const uuidFn = messages_1.IdGenerator.uuid(); | ||
const builder = new gherkin_1.AstBuilder(uuidFn); | ||
const matcher = new gherkin_1.GherkinClassicTokenMatcher(); // or GherkinInMarkdownTokenMatcher() | ||
const parser = new gherkin_1.Parser(builder, matcher); | ||
parse: (text, options) => { | ||
const uuidFn = IdGenerator.uuid(); | ||
const builder = new AstBuilder(uuidFn); | ||
const matcher = new GherkinClassicTokenMatcher(); // or GherkinInMarkdownTokenMatcher() | ||
const parser = new GherkinParser(builder, matcher); | ||
const document = parser.parse(text); | ||
@@ -71,4 +67,7 @@ generateColumnSizes(text); | ||
// ); | ||
return new GherkinAST_1.TypedGherkinDocument(Object.assign({}, document), { | ||
escapeBackslashes: (_a = options.escapeBackslashes) !== null && _a !== void 0 ? _a : DEFAULT_ESCAPE_BACKSLASH, | ||
return new TypedGherkinDocument({ | ||
...document, | ||
// comments: [], // igonre comments for now | ||
}, { | ||
escapeBackslashes: options.escapeBackslashes ?? DEFAULT_ESCAPE_BACKSLASH, | ||
}); | ||
@@ -81,3 +80,2 @@ }, | ||
// } | ||
var _a; | ||
// console.log('locStart', node); | ||
@@ -87,3 +85,3 @@ assertNodeHasLocation(node); | ||
let index = node.location.line > 1 ? textColumnWidth[node.location.line - 2] : 0; | ||
index += ((_a = node.location.column) !== null && _a !== void 0 ? _a : 1) - 1; | ||
index += (node.location.column ?? 1) - 1; | ||
return index; | ||
@@ -105,9 +103,9 @@ // if (node instanceof TypedComment) { | ||
// console.log('locEnd', node, '\n'); | ||
if (node instanceof GherkinAST_1.TypedComment) { | ||
if (node instanceof TypedComment) { | ||
return gherkinParser.locStart(node) + node.text.trim().length; | ||
} | ||
if (!(node instanceof GherkinAST_1.TypedComment)) { | ||
if (!(node instanceof TypedComment)) { | ||
console.log({ method: 'locEnd', nodeName: node.constructor.name }); | ||
} | ||
if (node instanceof GherkinAST_1.TypedFeature) { | ||
if (node instanceof TypedFeature) { | ||
return (gherkinParser.locStart(node) + | ||
@@ -154,6 +152,6 @@ node.keyword.length + | ||
const { line, column } = comment.location; | ||
if ((0, GherkinAST_1.isWithLocation)(ast) && ast.location.line > line) { | ||
if (isWithLocation(ast) && ast.location.line > line) { | ||
return ast; | ||
} | ||
if ((0, GherkinAST_1.isHasChild)(ast)) { | ||
if (isHasChild(ast)) { | ||
const { child } = ast; | ||
@@ -167,3 +165,3 @@ if (child) { | ||
} | ||
if ((0, GherkinAST_1.isHasChildren)(ast)) { | ||
if (isHasChildren(ast)) { | ||
const { children } = ast; | ||
@@ -183,3 +181,3 @@ for (const child of children) { | ||
node.keywordType && | ||
[messages_1.StepKeywordType.CONTEXT, messages_1.StepKeywordType.ACTION].includes(node.keywordType)); | ||
[StepKeywordType.CONTEXT, StepKeywordType.ACTION].includes(node.keywordType)); | ||
} | ||
@@ -190,3 +188,3 @@ const gherkinAstPrinter = { | ||
// console.log(node); | ||
if (!(node instanceof GherkinAST_1.TypedComment)) { | ||
if (!(node instanceof TypedComment)) { | ||
throw new Error('printComment: not a comment'); | ||
@@ -198,3 +196,3 @@ } | ||
// but no blank line between the comment and the step | ||
if (parentNode instanceof GherkinAST_1.TypedStep && | ||
if (parentNode instanceof TypedStep && | ||
stepNeedsHardline(parentNode, parentNodeIsFirstStep)) { | ||
@@ -205,6 +203,7 @@ return [printHardline(), node.text.trim()]; | ||
}, | ||
// canAttachComment(node): boolean { | ||
// console.log('canAttachComment', node); | ||
// return true; | ||
// }, | ||
canAttachComment(node) { | ||
// comments are all in the TypedGherkinDocument.comments array. | ||
// Do not handle comments in the subtree of the AST. | ||
return node instanceof TypedGherkinDocument; | ||
}, | ||
isBlockComment(node) { | ||
@@ -214,2 +213,6 @@ // // console.log('isBlockComment', node); | ||
}, | ||
// getCommentChildNodes: (node) => { | ||
// console.log('getCommentChildNodes', node) | ||
// return [] | ||
// }, | ||
handleComments: { | ||
@@ -219,3 +222,3 @@ ownLine: (commentNode, text, options, ast, isLastComment) => { | ||
if (node) { | ||
prettier_1.util.addLeadingComment(node, commentNode); | ||
util.addLeadingComment(node, commentNode); | ||
return true; | ||
@@ -230,3 +233,3 @@ } | ||
if (isRemainingLinesComments) { | ||
prettier_1.util.addTrailingComment(ast, commentNode); | ||
util.addTrailingComment(ast, commentNode); | ||
return true; | ||
@@ -241,3 +244,3 @@ } | ||
if (node) { | ||
prettier_1.util.addLeadingComment(node, commentNode); | ||
util.addLeadingComment(node, commentNode); | ||
return true; | ||
@@ -249,5 +252,5 @@ } | ||
remaining: (commentNode, text, options, ast, isLastComment) => { | ||
if (ast instanceof GherkinAST_1.TypedGherkinDocument && !ast.feature) { | ||
if (ast instanceof TypedGherkinDocument && !ast.feature) { | ||
// special case where the document only contains comments : let's attach the comment to the document directly | ||
prettier_1.util.addLeadingComment(ast, commentNode); | ||
util.addLeadingComment(ast, commentNode); | ||
return true; | ||
@@ -261,3 +264,4 @@ } | ||
// console.log({ node, isDocument: node instanceof TypedGherkinDocument }); | ||
if (node instanceof GherkinAST_1.TypedGherkinDocument) { | ||
if (node instanceof TypedGherkinDocument) { | ||
// console.log(node) | ||
if (node.feature) { | ||
@@ -268,10 +272,10 @@ // @ts-expect-error TODO path should be recognized as an AstPath<TypedGherkinDocument> | ||
else { | ||
// return empty sting if there is no feature (it can contain only comments) | ||
// return empty string if there is no feature (it can contain only comments) | ||
return ''; | ||
} | ||
} | ||
else if (node instanceof GherkinAST_1.TypedFeature || node instanceof GherkinAST_1.TypedRule) { | ||
else if (node instanceof TypedFeature || node instanceof TypedRule) { | ||
const hasLanguageInSourceFile = !!options.originalText.match(new RegExp(LANGUAGE_PATTERN, 'm')); | ||
return [ | ||
node instanceof GherkinAST_1.TypedFeature && hasLanguageInSourceFile | ||
node instanceof TypedFeature && hasLanguageInSourceFile | ||
? ['# language: ' + node.language, printHardline()] | ||
@@ -289,4 +293,4 @@ : '', | ||
} | ||
else if (node instanceof GherkinAST_1.TypedFeatureChild || | ||
node instanceof GherkinAST_1.TypedRuleChild) { | ||
else if (node instanceof TypedFeatureChild || | ||
node instanceof TypedRuleChild) { | ||
if (node.scenario) { | ||
@@ -300,3 +304,3 @@ // @ts-expect-error TODO path should be recognized as an AstPath<TypedFeatureChild> | ||
} | ||
else if (node instanceof GherkinAST_1.TypedFeatureChild && node.rule) { | ||
else if (node instanceof TypedFeatureChild && node.rule) { | ||
// @ts-expect-error TODO path should be recognized as an AstPath<TypedFeatureChild> | ||
@@ -307,3 +311,3 @@ return path.call(print, 'rule'); | ||
console.log(node); | ||
throw new Error(`unhandled case where ${node instanceof GherkinAST_1.TypedFeatureChild | ||
throw new Error(`unhandled case where ${node instanceof TypedFeatureChild | ||
? 'TypedFeatureChild' | ||
@@ -313,6 +317,6 @@ : 'TypedRuleChild'} has no scenario`); | ||
} | ||
else if (node instanceof GherkinAST_1.TypedTag) { | ||
else if (node instanceof TypedTag) { | ||
return node.name; | ||
} | ||
else if (node instanceof GherkinAST_1.TypedBackground) { | ||
else if (node instanceof TypedBackground) { | ||
// console.log(node.steps); | ||
@@ -331,3 +335,3 @@ return [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedScenario) { | ||
else if (node instanceof TypedScenario) { | ||
// console.log(node); | ||
@@ -350,3 +354,3 @@ return [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedStep) { | ||
else if (node instanceof TypedStep) { | ||
// console.log(node); | ||
@@ -370,3 +374,3 @@ return [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedDocString) { | ||
else if (node instanceof TypedDocString) { | ||
// console.log({ node }); | ||
@@ -389,3 +393,3 @@ // if the content contains the delimiter, the parser will unescape it, so we need to escape it again | ||
} | ||
else if (node instanceof GherkinAST_1.TypedExamples) { | ||
else if (node instanceof TypedExamples) { | ||
// console.log({ node, columnSizes: node.columnSizes }); | ||
@@ -407,3 +411,3 @@ return [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedDataTable) { | ||
else if (node instanceof TypedDataTable) { | ||
// console.log({ node, columnSizes: node.columnSizes }); | ||
@@ -415,3 +419,3 @@ return join(printHardline(), [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedTableRow) { | ||
else if (node instanceof TypedTableRow) { | ||
// console.log(node); | ||
@@ -425,3 +429,3 @@ return [ | ||
} | ||
else if (node instanceof GherkinAST_1.TypedTableCell) { | ||
else if (node instanceof TypedTableCell) { | ||
// console.log(node); | ||
@@ -435,24 +439,30 @@ return escapeMultilineString(node.value.padEnd(node.displaySize)); | ||
}, | ||
embed(path, print, textToDoc, options) { | ||
embed(path, options) { | ||
const node = path.getValue(); | ||
if (node instanceof GherkinAST_1.TypedDocString) { | ||
if (node instanceof TypedDocString) { | ||
const { content, mediaType } = node; | ||
if (mediaType === 'xml') { | ||
return [ | ||
node.delimiter, | ||
mediaType, | ||
printHardline(), | ||
textToDoc(content, Object.assign(Object.assign({}, options), { parser: 'html' })), | ||
node.delimiter, | ||
]; | ||
return async (textToDoc) => { | ||
return [ | ||
node.delimiter, | ||
mediaType, | ||
printHardline(), | ||
await textToDoc(content, { ...options, parser: 'html' }), | ||
printHardline(), | ||
node.delimiter, | ||
]; | ||
}; | ||
} | ||
try { | ||
JSON.parse(content); | ||
return [ | ||
node.delimiter, | ||
mediaType !== null && mediaType !== void 0 ? mediaType : '', | ||
printHardline(), | ||
textToDoc(content, Object.assign(Object.assign({}, options), { parser: 'json' })), | ||
node.delimiter, | ||
]; | ||
return async (textToDoc) => { | ||
return [ | ||
node.delimiter, | ||
mediaType ?? '', | ||
printHardline(), | ||
await textToDoc(content, { ...options, parser: 'json' }), | ||
printHardline(), | ||
node.delimiter, | ||
]; | ||
}; | ||
} | ||
@@ -484,3 +494,2 @@ catch (e) { | ||
oppositeDescription: 'Do not escape backslashes in strings', | ||
since: '1.0.0', | ||
category: 'Format', | ||
@@ -490,2 +499,2 @@ }, | ||
}; | ||
module.exports = plugin; | ||
export default plugin; |
{ | ||
"name": "prettier-plugin-gherkin", | ||
"version": "1.1.1", | ||
"version": "2.0.0", | ||
"type": "module", | ||
"description": "This prettier plugin format your gherkin (`.feature` files) documents.", | ||
@@ -9,4 +10,4 @@ "main": "dist/index.js", | ||
"lint": "tsc --noEmit", | ||
"example": "yarn build && prettier --plugin . example.feature", | ||
"test": "jest", | ||
"example": "yarn build && prettier --plugin=dist/index.js tests/plugin/example.feature", | ||
"test": "NODE_OPTIONS=--experimental-vm-modules jest", | ||
"prepublishOnly": "yarn build" | ||
@@ -27,11 +28,10 @@ }, | ||
"@cucumber/messages": "^19.1.4", | ||
"prettier": "^2.7.1" | ||
"prettier": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^18.11.7", | ||
"@types/prettier": "^2.7.1", | ||
"jest": "^24.0.0", | ||
"jest-watch-typeahead": "^0.2.1", | ||
"jest": "^29.0.0", | ||
"jest-watch-typeahead": "^2.2.2", | ||
"typescript": "^4.8.4" | ||
} | ||
} |
@@ -8,4 +8,3 @@ # prettier-plugin-gherkin | ||
```gherkin | ||
@accountability | ||
@accountability-json | ||
@accountability @accountability-json | ||
Feature: accountability | ||
@@ -25,2 +24,3 @@ accounts should always be good | ||
# I will create an order here | ||
Given I create an order | ||
@@ -57,2 +57,3 @@ Then the response status code should be 200 | ||
# I will create an order here | ||
Given I create an order | ||
@@ -64,10 +65,30 @@ Then the response status code should be 200 | ||
""" | ||
[ | ||
{"name": "card", | ||
"credit": 5000 | ||
} | ||
] | ||
[{ "name": "card", "credit": 5000 }] | ||
""" | ||
``` | ||
### Versions | ||
This version is compatible with prettier 3. If you still use prettier 2, you should use the [1.1.1](https://github.com/mapado/prettier-plugin-gherkin/blob/main/CHANGELOG.md#111) version of this plugin. | ||
## Installation | ||
Install [prettier](https://prettier.io/docs/en/install.html) if you don't have already. | ||
```sh | ||
npm install --save prettier-plugin-gherkin | ||
// or with yarn | ||
yarn add prettier-plugin-gherkin | ||
``` | ||
That's all ! the plugin will be automatically detected by prettier. | ||
You can then configure your git precommit, IDE, etc. to format the `.features` file with prettier. | ||
### Example with VSCode | ||
https://user-images.githubusercontent.com/1398469/201128147-ad2ecba8-253d-4c70-9133-ae28d178ed2b.mp4 | ||
## Options | ||
@@ -107,4 +128,22 @@ | ||
## Test datas | ||
## Contributing to this plugin | ||
Test datas can be found here : https://github.com/cucumber/common/tree/main/gherkin/testdata | ||
Clone the repository and install dependencies: | ||
```sh | ||
git clone git@github.com:mapado/prettier-plugin-gherkin.git | ||
cd prettier-plugin-gherkin | ||
yarn install | ||
``` | ||
Several commands are available: | ||
```sh | ||
yarn example # print the example file with the plugin. You can change de content of the example file as you need | ||
yarn test # run the tests suites : all features are tested | ||
yarn lint # the plugin is writter in TypeScript, this command will help you detect typescript issue | ||
``` | ||
### Test datas | ||
Test datas can be found here : https://github.com/cucumber/gherkin-utils/tree/main/testdata |
@@ -22,2 +22,3 @@ import { | ||
ParserOptions, | ||
Options, | ||
} from 'prettier'; | ||
@@ -48,3 +49,3 @@ import { | ||
isWithLocation, | ||
} from './GherkinAST'; | ||
} from './GherkinAST/index.js'; | ||
@@ -56,4 +57,3 @@ const DEFAULT_ESCAPE_BACKSLASH = false; | ||
const { literalline, hardline, join, group, trim, indent, line } = doc.builders; | ||
const { hasNewline, isPreviousLineEmpty, makeString } = util; | ||
const { hardline, join, indent } = doc.builders; | ||
@@ -120,7 +120,3 @@ const languages: SupportLanguage[] = [ | ||
const gherkinParser: Parser<GherkinNode> = { | ||
parse: ( | ||
text: string, | ||
parsers, | ||
options: GherkinParseOptions | ||
): GherkinDocument => { | ||
parse: (text: string, options: GherkinParseOptions): GherkinDocument => { | ||
const uuidFn = IdGenerator.uuid(); | ||
@@ -337,6 +333,7 @@ const builder = new AstBuilder(uuidFn); | ||
// canAttachComment(node): boolean { | ||
// console.log('canAttachComment', node); | ||
// return true; | ||
// }, | ||
canAttachComment(node): boolean { | ||
// comments are all in the TypedGherkinDocument.comments array. | ||
// Do not handle comments in the subtree of the AST. | ||
return node instanceof TypedGherkinDocument; | ||
}, | ||
@@ -348,2 +345,10 @@ isBlockComment(node): boolean { | ||
// getCommentChildNodes: (node) => { | ||
// console.log('getCommentChildNodes', node) | ||
// return [] | ||
// }, | ||
handleComments: { | ||
@@ -389,2 +394,3 @@ ownLine: ( | ||
const node = findNodeForCommentInAST(ast, commentNode); | ||
if (node) { | ||
@@ -424,2 +430,3 @@ util.addLeadingComment(node, commentNode); | ||
if (node instanceof TypedGherkinDocument) { | ||
// console.log(node) | ||
if (node.feature) { | ||
@@ -429,3 +436,3 @@ // @ts-expect-error TODO path should be recognized as an AstPath<TypedGherkinDocument> | ||
} else { | ||
// return empty sting if there is no feature (it can contain only comments) | ||
// return empty string if there is no feature (it can contain only comments) | ||
return ''; | ||
@@ -595,3 +602,3 @@ } | ||
embed(path: AstPath, print, textToDoc, options: object): Doc | null { | ||
embed(path: AstPath, options: Options) { | ||
const node = path.getValue(); | ||
@@ -602,9 +609,12 @@ if (node instanceof TypedDocString) { | ||
if (mediaType === 'xml') { | ||
return [ | ||
node.delimiter, | ||
mediaType, | ||
printHardline(), | ||
textToDoc(content, { ...options, parser: 'html' }), | ||
node.delimiter, | ||
]; | ||
return async (textToDoc): Promise<Doc> => { | ||
return [ | ||
node.delimiter, | ||
mediaType, | ||
printHardline(), | ||
await textToDoc(content, { ...options, parser: 'html' }), | ||
printHardline(), | ||
node.delimiter, | ||
]; | ||
}; | ||
} | ||
@@ -615,9 +625,12 @@ | ||
return [ | ||
node.delimiter, | ||
mediaType ?? '', | ||
printHardline(), | ||
textToDoc(content, { ...options, parser: 'json' }), | ||
node.delimiter, | ||
]; | ||
return async (textToDoc): Promise<Doc> => { | ||
return [ | ||
node.delimiter, | ||
mediaType ?? '', | ||
printHardline(), | ||
await textToDoc(content, { ...options, parser: 'json' }), | ||
printHardline(), | ||
node.delimiter, | ||
]; | ||
}; | ||
} catch (e) { | ||
@@ -650,3 +663,2 @@ // igonre non-JSON content | ||
oppositeDescription: 'Do not escape backslashes in strings', | ||
since: '1.0.0', | ||
category: 'Format', | ||
@@ -657,2 +669,2 @@ }, | ||
module.exports = plugin; | ||
export default plugin; |
@@ -17,3 +17,3 @@ // prettier-ignore | ||
/* Language and Environment */ | ||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | ||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ | ||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ | ||
@@ -32,5 +32,5 @@ // "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
/* Modules */ | ||
"module": "commonjs", /* Specify what module code is generated. */ | ||
"module": "ES2022", /* Specify what module code is generated. */ | ||
// "rootDir": "./", /* Specify the root folder within your source files. */ | ||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ | ||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ | ||
@@ -37,0 +37,0 @@ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
High entropy strings
Supply chain riskContains high entropy strings. This could be a sign of encrypted data, leaked secrets or obfuscated code.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18804819
4
759
149254
145
Yes
3
70
38
2
+ Addedprettier@3.3.3(transitive)
- Removedprettier@2.8.8(transitive)
Updatedprettier@^3.0.0