Skip to content

Commit

Permalink
Fix formatter bugs (#213)
Browse files Browse the repository at this point in the history
* fix formatter bugs

* move isIdentifier to util

* clean up
  • Loading branch information
mfix-stripe committed Sep 22, 2022
1 parent ef9939f commit e81504b
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 10 deletions.
21 changes: 16 additions & 5 deletions src/formatter.test.ts
Expand Up @@ -84,13 +84,11 @@ function stable(source, options?) {
}

describe('Formatter', () => {
it('null', () => {
expect(format(null)).toBe('');
});

it('empty', () => {
expect(format(null)).toBe('');
check('', '');
stable('');
check('\n\n\t\n \n \n\n', '');
stable('\n\n\t\n \n \n\n');
});

it('basics', () => {
Expand All @@ -109,6 +107,16 @@ subtitle: Subtitle
stable(source);
});

it('complex attributes', () => {
const source = `{% if $gates["<string_key>"].test["@var"] id="id with space" class="class with space" /%}`;
const expected = `{% if
$gates["<string_key>"].test["@var"]
id="id with space"
class="class with space" /%}
`;
check(source, expected);
});

it('attribute edge cases', () => {
const source = `{% key id=$user.name class=default($y, "test") %}Child{% /key %}`;
const expected = `
Expand All @@ -121,13 +129,16 @@ subtitle: Subtitle

it('variables', () => {
const source = `
{% tag "complex primary" /%}
{% if $primary %}
X
{% /if %}
{% $user.name %}
{% key x=$user.name new=$flag /%}
`;
const expected = `
{% tag "complex primary" /%}
{% if $primary %}
X
{% /if %}
Expand Down
21 changes: 16 additions & 5 deletions src/formatter.ts
@@ -1,5 +1,5 @@
import Ast from './ast';
import { OPEN, CLOSE } from './utils';
import { OPEN, CLOSE, isIdentifier } from './utils';
import type { AttributeValue, Function, Node, Value, Variable } from './types';

type Options = {
Expand Down Expand Up @@ -57,14 +57,19 @@ function formatScalar(v: Value): string {

function formatAnnotationValue(a: AttributeValue): string {
if (a.name === 'primary') return formatScalar(a.value);
if (a.name === 'id' && typeof a.value === 'string') return '#' + a.value;
if (a.type === 'class') return '.' + a.name;
if (a.name === 'id' && typeof a.value === 'string' && isIdentifier(a.value))
return '#' + a.value;
if (a.type === 'class' && isIdentifier(a.name)) return '.' + a.name;
return `${a.name}=${formatScalar(a.value)}`;
}

function* formatAttributes(n: Node) {
for (const [key, value] of Object.entries(n.attributes)) {
if (key === 'class' && !Ast.isAst(value))
/**
* In cases where the class attribute is not a valid identifer, we treat it as a
* regular attribute without the '.' sigil
*/
if (key === 'class' && typeof value === 'object' && !Ast.isAst(value))
for (const name of Object.keys(value)) {
yield formatAnnotationValue({ type: 'class', name, value });
}
Expand All @@ -82,7 +87,13 @@ function* formatAnnotations(n: Node) {

function* formatVariable(v: Variable) {
yield '$';
yield v.path.join('.');
yield v.path
.map((p, i) => {
if (i === 0) return p;
if (isIdentifier(String(p))) return '.' + p;
return `["${p}"]`;
})
.join('');
}

function* formatFunction(f: Function) {
Expand Down
6 changes: 6 additions & 0 deletions src/utils.ts
Expand Up @@ -13,6 +13,12 @@ enum STATES {
export const OPEN = '{%';
export const CLOSE = '%}';

export const IDENTIFIER_REGEX = /^[a-zA-Z0-9_-]+$/;

export function isIdentifier(s: string): boolean {
return IDENTIFIER_REGEX.test(s);
}

export function isPromise(a: any): a is Promise<any> {
return a && typeof a === 'object' && typeof a.then === 'function';
}
Expand Down

0 comments on commit e81504b

Please sign in to comment.