From dd7df8ebfe86a96ff3b9ce2ded5fd9d67aa2b0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Mon, 28 Sep 2020 00:29:00 +0200 Subject: [PATCH 1/4] Allow "none" as an option for attributeSeparator Closes #102 --- README.md | 2 ++ src/options/attribute-separator.ts | 20 +++++++++++++----- src/printer.ts | 7 +++++-- .../attributeSeparator/none/formatted.pug | 12 +++++++++++ .../attributeSeparator/none/none.test.ts | 21 +++++++++++++++++++ .../attributeSeparator/none/unformatted.pug | 5 +++++ 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 tests/options/attributeSeparator/none/formatted.pug create mode 100644 tests/options/attributeSeparator/none/none.test.ts create mode 100644 tests/options/attributeSeparator/none/unformatted.pug diff --git a/README.md b/README.md index b08c7821..2c73cb98 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ They should be set via `Prettier`'s `overrides` option Example: `button(type="submit", (click)="play()", disabled)` - `'as-needed'` -> Only add commas between attributes where required. Example: `button(type="submit", (click)="play()" disabled)` + - `'none'` -> Never add commas between attributes. + Example: `button(type="submit" @click="play()" :style="style" disabled)` - `closingBracketPosition` Position of closing bracket of attributes. diff --git a/src/options/attribute-separator.ts b/src/options/attribute-separator.ts index 568ad840..02e2456e 100644 --- a/src/options/attribute-separator.ts +++ b/src/options/attribute-separator.ts @@ -16,6 +16,11 @@ export const ATTRIBUTE_SEPARATOR_OPTION = { value: 'as-needed', description: 'Only add commas between attributes where required. Example: `button(type="submit", (click)="play()" disabled)`' + }, + { + value: 'none', + description: + 'Never add commas between attributes. Example: `button(type="submit" @click="play()" :style="style" disabled)`' } ] }; @@ -38,20 +43,25 @@ export const PUG_ATTRIBUTE_SEPARATOR_OPTION = { value: 'as-needed', description: 'Only add commas between attributes where required. Example: `button(type="submit", (click)="play()" disabled)`' + }, + { + value: 'none', + description: + 'Never add commas between attributes. Example: `button(type="submit" @click="play()" :style="style" disabled)`' } ] }; -export type AttributeSeparator = 'always' | 'as-needed'; +export type AttributeSeparator = 'always' | 'as-needed' | 'none'; -export function resolveAttributeSeparatorOption(attributeSeparator: AttributeSeparator): boolean { +export function resolveAttributeSeparatorOption(attributeSeparator: AttributeSeparator): AttributeSeparator { switch (attributeSeparator) { case 'always': - return true; case 'as-needed': - return false; + case 'none': + return attributeSeparator } throw new Error( - `Invalid option for pug attributeSeparator. Found '${attributeSeparator}'. Possible options: 'always' or 'as-needed'` + `Invalid option for pug attributeSeparator. Found '${attributeSeparator}'. Possible options: 'always', 'as-needed' or 'none'` ); } diff --git a/src/printer.ts b/src/printer.ts index e6d1ef53..4b66f58d 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -103,6 +103,7 @@ export class PugPrinter { private readonly otherQuotes: "'" | '"'; private readonly alwaysUseAttributeSeparator: boolean; + private readonly neverUseAttributeSeparator: boolean; private readonly closingBracketRemainsAtNewLine: boolean; private readonly codeInterpolationOptions: Pick; @@ -119,7 +120,9 @@ export class PugPrinter { this.indentString = options.pugUseTabs ? '\t' : ' '.repeat(options.pugTabWidth); this.quotes = this.options.pugSingleQuote ? "'" : '"'; this.otherQuotes = this.options.pugSingleQuote ? '"' : "'"; - this.alwaysUseAttributeSeparator = resolveAttributeSeparatorOption(options.attributeSeparator); + const attributeSeparator = resolveAttributeSeparatorOption(options.attributeSeparator); + this.alwaysUseAttributeSeparator = attributeSeparator === 'always'; + this.neverUseAttributeSeparator = attributeSeparator === 'none'; this.closingBracketRemainsAtNewLine = resolveClosingBracketPositionOption(options.closingBracketPosition); const codeSingleQuote = !options.pugSingleQuote; this.codeInterpolationOptions = { @@ -525,7 +528,7 @@ export class PugPrinter { this.currentIndex ); if (this.previousToken?.type === 'attribute' && (!this.previousAttributeRemapped || hasNormalPreviousToken)) { - if (this.alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name)) { + if (!this.neverUseAttributeSeparator && (this.alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name))) { this.result += ','; } if (!this.wrapAttributes) { diff --git a/tests/options/attributeSeparator/none/formatted.pug b/tests/options/attributeSeparator/none/formatted.pug new file mode 100644 index 00000000..9c3b283b --- /dev/null +++ b/tests/options/attributeSeparator/none/formatted.pug @@ -0,0 +1,12 @@ +button(type="submit" :style="style" @click="play()" disabled) + +nav-component(locale-relative-redirect="true" highlight="home" pin="false") + +.wrapper( + data-nav + data-next="Next" + data-prev="Prev" + data-slides=4 + data-arr=[1, 2, 3, 4, 5, 6] + data-obj={ att: 1, attr2: 2 } +) diff --git a/tests/options/attributeSeparator/none/none.test.ts b/tests/options/attributeSeparator/none/none.test.ts new file mode 100644 index 00000000..145f698a --- /dev/null +++ b/tests/options/attributeSeparator/none/none.test.ts @@ -0,0 +1,21 @@ +import { readFileSync } from 'fs'; +import { resolve } from 'path'; +import { format } from 'prettier'; +import { plugin } from './../../../../src/index'; + +describe('Options', () => { + describe('attributeSeparator', () => { + test('should never insert commas between attributes', () => { + const expected: string = readFileSync(resolve(__dirname, 'formatted.pug'), 'utf8'); + const code: string = readFileSync(resolve(__dirname, 'unformatted.pug'), 'utf8'); + const actual: string = format(code, { + parser: 'pug' as any, + plugins: [plugin], + // @ts-expect-error + attributeSeparator: 'none' + }); + + expect(actual).toBe(expected); + }); + }); +}); diff --git a/tests/options/attributeSeparator/none/unformatted.pug b/tests/options/attributeSeparator/none/unformatted.pug new file mode 100644 index 00000000..3f2f006c --- /dev/null +++ b/tests/options/attributeSeparator/none/unformatted.pug @@ -0,0 +1,5 @@ +button(type="submit", :style="style", @click="play()", disabled) + +nav-component(locale-relative-redirect="true", highlight="home", pin="false") + +.wrapper(data-nav=true data-next="Next" data-prev="Prev" data-slides=4 data-arr=[1,2,3,4,5,6] data-obj={att: 1, attr2: 2}) From 3b3ae7b76358103df7b8f8ee2dc606d23e553fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Mon, 28 Sep 2020 22:55:02 +0200 Subject: [PATCH 2/4] Add unit tests and note for angular syntax --- README.md | 1 + .../none/angular-formatted.pug | 1 + .../none/angular-unformatted.pug | 1 + .../attributeSeparator/none/formatted.pug | 2 ++ .../attributeSeparator/none/none.test.ts | 19 ++++++++++++++++++- .../attributeSeparator/none/unformatted.pug | 2 +- 6 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/options/attributeSeparator/none/angular-formatted.pug create mode 100644 tests/options/attributeSeparator/none/angular-unformatted.pug diff --git a/README.md b/README.md index 2c73cb98..91641c9d 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ They should be set via `Prettier`'s `overrides` option Example: `button(type="submit", (click)="play()" disabled)` - `'none'` -> Never add commas between attributes. Example: `button(type="submit" @click="play()" :style="style" disabled)` + Please note that while this option will process Angular syntax (e.g. `(click)="play()"`), the resulting pug file will throw a syntax error when parsed: `Syntax Error: Assigning to rvalue` - `closingBracketPosition` Position of closing bracket of attributes. diff --git a/tests/options/attributeSeparator/none/angular-formatted.pug b/tests/options/attributeSeparator/none/angular-formatted.pug new file mode 100644 index 00000000..43eaf8a3 --- /dev/null +++ b/tests/options/attributeSeparator/none/angular-formatted.pug @@ -0,0 +1 @@ +button(type="submit" (click)="play()" disabled) diff --git a/tests/options/attributeSeparator/none/angular-unformatted.pug b/tests/options/attributeSeparator/none/angular-unformatted.pug new file mode 100644 index 00000000..956bc647 --- /dev/null +++ b/tests/options/attributeSeparator/none/angular-unformatted.pug @@ -0,0 +1 @@ +button(type="submit", (click)="play()", disabled) diff --git a/tests/options/attributeSeparator/none/formatted.pug b/tests/options/attributeSeparator/none/formatted.pug index 9c3b283b..90500698 100644 --- a/tests/options/attributeSeparator/none/formatted.pug +++ b/tests/options/attributeSeparator/none/formatted.pug @@ -9,4 +9,6 @@ nav-component(locale-relative-redirect="true" highlight="home" pin="false") data-slides=4 data-arr=[1, 2, 3, 4, 5, 6] data-obj={ att: 1, attr2: 2 } + :style="style" + @click="play()" ) diff --git a/tests/options/attributeSeparator/none/none.test.ts b/tests/options/attributeSeparator/none/none.test.ts index 145f698a..f491b3e3 100644 --- a/tests/options/attributeSeparator/none/none.test.ts +++ b/tests/options/attributeSeparator/none/none.test.ts @@ -1,7 +1,10 @@ import { readFileSync } from 'fs'; import { resolve } from 'path'; import { format } from 'prettier'; -import { plugin } from './../../../../src/index'; +import { plugin, parsers } from './../../../../src/index'; +import { Parser } from 'prettier'; + +/* eslint @typescript-eslint/no-non-null-assertion: off */ describe('Options', () => { describe('attributeSeparator', () => { @@ -17,5 +20,19 @@ describe('Options', () => { expect(actual).toBe(expected); }); + test(`should work with 'none' option and angular syntax, but produce invalid output`, () => { + const expected: string = readFileSync(resolve(__dirname, 'angular-formatted.pug'), 'utf8'); + const code: string = readFileSync(resolve(__dirname, 'angular-unformatted.pug'), 'utf8'); + const actual: string = format(code, { + parser: 'pug' as any, + plugins: [plugin], + // @ts-expect-error + attributeSeparator: 'none' + }); + expect(actual).toBe(expected); + expect(() => { + parsers!.pug.parse(actual, parsers!, null!); + }).toThrow('Assigning to rvalue'); + }); }); }); diff --git a/tests/options/attributeSeparator/none/unformatted.pug b/tests/options/attributeSeparator/none/unformatted.pug index 3f2f006c..77f5318f 100644 --- a/tests/options/attributeSeparator/none/unformatted.pug +++ b/tests/options/attributeSeparator/none/unformatted.pug @@ -2,4 +2,4 @@ button(type="submit", :style="style", @click="play()", disabled) nav-component(locale-relative-redirect="true", highlight="home", pin="false") -.wrapper(data-nav=true data-next="Next" data-prev="Prev" data-slides=4 data-arr=[1,2,3,4,5,6] data-obj={att: 1, attr2: 2}) +.wrapper(data-nav=true data-next="Next" data-prev="Prev" data-slides=4 data-arr=[1,2,3,4,5,6] data-obj={att: 1, attr2: 2} :style="style" @click="play()") From 022321da978baf37fae8ea82b69836d383cdf537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Mon, 28 Sep 2020 22:56:12 +0200 Subject: [PATCH 3/4] Remove unused import --- tests/options/attributeSeparator/none/none.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/options/attributeSeparator/none/none.test.ts b/tests/options/attributeSeparator/none/none.test.ts index f491b3e3..296f9252 100644 --- a/tests/options/attributeSeparator/none/none.test.ts +++ b/tests/options/attributeSeparator/none/none.test.ts @@ -2,9 +2,6 @@ import { readFileSync } from 'fs'; import { resolve } from 'path'; import { format } from 'prettier'; import { plugin, parsers } from './../../../../src/index'; -import { Parser } from 'prettier'; - -/* eslint @typescript-eslint/no-non-null-assertion: off */ describe('Options', () => { describe('attributeSeparator', () => { From a81ae045e799c7f9fdae803d1123941936b87182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Mon, 28 Sep 2020 23:12:08 +0200 Subject: [PATCH 4/4] Fix linting errors --- src/options/attribute-separator.ts | 4 ++-- tests/options/attributeSeparator/none/none.test.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/options/attribute-separator.ts b/src/options/attribute-separator.ts index 02e2456e..f2172a20 100644 --- a/src/options/attribute-separator.ts +++ b/src/options/attribute-separator.ts @@ -58,8 +58,8 @@ export function resolveAttributeSeparatorOption(attributeSeparator: AttributeSep switch (attributeSeparator) { case 'always': case 'as-needed': - case 'none': - return attributeSeparator + case 'none': + return attributeSeparator; } throw new Error( `Invalid option for pug attributeSeparator. Found '${attributeSeparator}'. Possible options: 'always', 'as-needed' or 'none'` diff --git a/tests/options/attributeSeparator/none/none.test.ts b/tests/options/attributeSeparator/none/none.test.ts index 296f9252..3eb80d5d 100644 --- a/tests/options/attributeSeparator/none/none.test.ts +++ b/tests/options/attributeSeparator/none/none.test.ts @@ -17,7 +17,7 @@ describe('Options', () => { expect(actual).toBe(expected); }); - test(`should work with 'none' option and angular syntax, but produce invalid output`, () => { + test('should work with \'none\' option and angular syntax, but produce invalid output', () => { const expected: string = readFileSync(resolve(__dirname, 'angular-formatted.pug'), 'utf8'); const code: string = readFileSync(resolve(__dirname, 'angular-unformatted.pug'), 'utf8'); const actual: string = format(code, { @@ -28,6 +28,7 @@ describe('Options', () => { }); expect(actual).toBe(expected); expect(() => { + /* eslint @typescript-eslint/no-non-null-assertion: off */ parsers!.pug.parse(actual, parsers!, null!); }).toThrow('Assigning to rvalue'); });