Skip to content

Commit

Permalink
Multiple fixes for class attribute conversions (#509)
Browse files Browse the repository at this point in the history
Co-authored-by: Shinigami92 <chrissi92@hotmail.de>
  • Loading branch information
MrHaila and Shinigami92 authored Oct 11, 2024
1 parent e3fc15c commit f0aa522
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 11 deletions.
65 changes: 57 additions & 8 deletions src/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1274,24 +1274,73 @@ export class PugPrinter {
private class(token: ClassToken): void {
if (this.options.pugClassNotation === 'attribute') {
this.classLiteralToAttribute.push(token.val);

// An extra div should be printed if...
if (
this.previousToken?.type !== 'tag' &&
this.previousToken?.type !== 'class'
this.previousToken === undefined ||
// ...the previous token indicates that this was the first class literal and thus a div did not previously exist...
this.checkTokenType(
this.previousToken,
['tag', 'class', 'end-attributes'],
true,
) ||
// ...OR the previous token is a div that will be removed because of the no explicit divs rule.
(this.previousToken.type === 'tag' &&
this.previousToken.val === 'div' &&
this.nextToken?.type !== 'attribute' &&
!this.options.pugExplicitDiv)
) {
this.result += `${this.computedIndent}div`;
}

if (
this.nextToken &&
['text', 'newline', 'indent', 'outdent', 'eos'].includes(
this.nextToken.type,
)
this.checkTokenType(this.nextToken, [
'text',
'newline',
'indent',
'outdent',
'eos',
':',
])
) {
// Copy and clear the class literals list.
const classes: string[] = this.classLiteralToAttribute.splice(
0,
this.classLiteralToAttribute.length,
);
this.result += `(class=${this.quoteString(classes.join(' '))})`;
if (this.nextToken.type === 'text') {

// If the last result character was a )...
if (this.result.at(-1) === ')') {
// Look for 'class=' that is before the last '('...
const attributesStartIndex: number = this.result.lastIndexOf('(');
const lastClassIndex: number = this.result.indexOf(
'class=',
attributesStartIndex,
);

// If a 'class=' is found...
// eslint-disable-next-line unicorn/prefer-ternary -- This is more readable without ternaries.
if (lastClassIndex > -1) {
// ...then insert the new class into it.
this.result = [
this.result.slice(0, lastClassIndex + 7),
classes.join(' '),
' ',
this.result.slice(lastClassIndex + 7),
].join('');
} else {
// ...otherwise add a new class attribute into the existing attributes.
this.result =
this.result.slice(0, -1) +
`${this.neverUseAttributeSeparator ? ' ' : ', '}class=${this.quoteString(classes.join(' '))})`;
}
// ...or if the element has no attributes...
} else {
// Start a new attribute list with the class attribute in it.
this.result += `(class=${this.quoteString(classes.join(' '))})`;
}

if (this.nextToken?.type === 'text') {
this.result += ' ';
}
}
Expand Down
55 changes: 55 additions & 0 deletions tests/options/pugClassNotation/formatted-as-is.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
input.bar.baz(class="foo")
input.bar.baz(
readonly,
class="foo",
name="test-input",
type="password",
value="Hello World"
)
.foo bar
.foo(bar="baz") Hello World
.foo
div(class="bar") baz
.baz
.bar foo
- var code = "ma-2 px-1";
div(class=code)
.foo
div.foo(attribute="value")
div.foo(class="bar", attribute="value")
div.foo.baz(attribute="value", class="bar")
div.foo.baz(attribute="value", class="bar")
.foo.baz(attribute="value", class="bar")
div.bar(class="foo"): span Text
div: span.foo
div: span(class="foo")
.foo: span.bar.foo(class="baz")
.foo.baz(class="bar"): h1.foo.baz(class="bar") Let's really #[span.foo.baz(class="bar") get nasty]!
.indent
input.bar.baz(class="foo")
input.bar.baz(
readonly,
class="foo",
name="test-input",
type="password",
value="Hello World"
)
.foo bar
.foo(bar="baz") Hello World
.foo
div(class="bar") baz
.baz
.bar foo
- var code = "ma-2 px-1";
div(class=code)
.foo
div.foo(attribute="value")
div.foo(class="bar", attribute="value")
div.foo.baz(attribute="value", class="bar")
div.foo.baz(attribute="value", class="bar")
.foo.baz(attribute="value", class="bar")
div.bar(class="foo"): span Text
div: span.foo
div: span(class="foo")
.foo: span.bar.foo(class="baz")
.foo.baz(class="bar"): h1.foo.baz(class="bar") Let's really #[span.foo.baz(class="bar") get nasty]!
33 changes: 33 additions & 0 deletions tests/options/pugClassNotation/formatted-attribute.pug
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,36 @@ div(class="baz")
div(class="bar") foo
- var code = "ma-2 px-1";
div(class=code)
div(class="foo")
div(attribute="value", class="foo")
div(class="foo bar", attribute="value")
div(attribute="value", class="foo baz bar")
div(attribute="value", class="foo baz bar")
div(attribute="value", class="baz foo bar")
div(class="bar foo"): span Text
div: span(class="foo")
div: span(class="foo")
div(class="foo"): span(class="foo bar baz")
div(class="baz foo bar"): h1(class="baz foo bar") Let's really #[span(class="baz foo bar") get nasty]!
div(class="indent")
input(class="bar baz foo")
input(readonly, class="bar baz foo", name="test-input", type="password", value="Hello World")
div(class="foo") bar
div(bar="baz" class="foo") Hello World
div(class="foo")
div(class="bar") baz
div(class="baz")
div(class="bar") foo
- var code = "ma-2 px-1";
div(class=code)
div(class="foo")
div(attribute="value", class="foo")
div(class="foo bar", attribute="value")
div(attribute="value", class="foo baz bar")
div(attribute="value", class="foo baz bar")
div(attribute="value", class="baz foo bar")
div(class="bar foo"): span Text
div: span(class="foo")
div: span(class="foo")
div(class="foo"): span(class="foo bar baz")
div(class="baz foo bar"): h1(class="baz foo bar") Let's really #[span(class="baz foo bar") get nasty]!
38 changes: 38 additions & 0 deletions tests/options/pugClassNotation/formatted-literal.pug
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,41 @@ input.bar.baz.foo(
.bar foo
- var code = "ma-2 px-1";
div(class=code)
.foo
div.foo(attribute="value")
.bar.foo(attribute="value")
.bar.foo.baz(attribute="value")
.bar.foo.baz(attribute="value")
.foo.bar.baz(attribute="value")
.foo.bar: span Text
div: span.foo
div: span.foo
.foo: span.bar.baz.foo
.foo.bar.baz: h1.foo.bar.baz Let's really #[span.foo.bar.baz get nasty]!
.indent
input.bar.baz.foo
input.bar.baz.foo(
readonly,
name="test-input",
type="password",
value="Hello World"
)
.foo bar
.foo(bar="baz") Hello World
.foo
.bar baz
.baz
.bar foo
- var code = "ma-2 px-1";
div(class=code)
.foo
div.foo(attribute="value")
.bar.foo(attribute="value")
.bar.foo.baz(attribute="value")
.bar.foo.baz(attribute="value")
.foo.bar.baz(attribute="value")
.foo.bar: span Text
div: span.foo
div: span.foo
.foo: span.bar.baz.foo
.foo.bar.baz: h1.foo.bar.baz Let's really #[span.foo.bar.baz get nasty]!
6 changes: 3 additions & 3 deletions tests/options/pugClassNotation/pug-class-notation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { describe, expect, it } from 'vitest';
describe('Options', () => {
describe('pugClassNotation', () => {
it('should keep classes as is', async () => {
const { actual, code } = await compareFiles(import.meta.url, {
target: null,
const { actual, expected } = await compareFiles(import.meta.url, {
target: 'formatted-as-is.pug',
formatOptions: {
pugClassNotation: 'as-is',
},
});
expect(actual).toBe(code);
expect(actual).toBe(expected);
});

it('should keep classes as literals', async () => {
Expand Down
45 changes: 45 additions & 0 deletions tests/options/pugClassNotation/unformatted.pug
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,48 @@ input.bar.baz(
.bar foo
- var code = "ma-2 px-1";
div(class=code)
div.foo
div(attribute="value").foo
div(class="bar", attribute="value").foo
div(attribute="value", class="bar").foo.baz
div(
attribute="value"
class="bar"
).foo.baz
div.foo(attribute="value" class="bar").baz
div(class="foo").bar: span Text
div: span.foo
div: span(class="foo")
div.foo: span.bar(class="baz").foo
div.foo(class="bar").baz: h1.foo(class="bar").baz Let's really #[span.foo(class="bar").baz get nasty]!
.indent
input.bar.baz(class="foo")
input.bar.baz(
readonly,
class="foo",
name="test-input",
type="password",
value="Hello World"
)
.foo bar
.foo(bar="baz") Hello World
.foo
div(class="bar") baz
.baz
.bar foo
- var code = "ma-2 px-1";
div(class=code)
div.foo
div(attribute="value").foo
div(class="bar", attribute="value").foo
div(attribute="value", class="bar").foo.baz
div(
attribute="value"
class="bar"
).foo.baz
div.foo(attribute="value" class="bar").baz
div(class="foo").bar: span Text
div: span.foo
div: span(class="foo")
div.foo: span.bar(class="baz").foo
div.foo(class="bar").baz: h1.foo(class="bar").baz Let's really #[span.foo(class="bar").baz get nasty]!

0 comments on commit f0aa522

Please sign in to comment.